Optional parameters and inheritance
Asked Answered
S

2

8

I understand optional parameters, and I quite like them, but I'd just like to know a bit more about using them with an inherited interface.

Exhibit A

interface IMyInterface
{
    string Get();
    string Get(string str);
}

class MyClass : IMyInterface
{
    public string Get(string str = null)
    {
        return str;
    }
}

Now I would've thought that the Get method in MyClass inherits both of the interface's methods, but...

'MyClass' does not implement interface member 'MyInterface.Get()'

Is there a good reason for this?

Perhaps I should go about this by putting the optional parameters in the interface you say? But what about this?

Exhibit B

interface IMyInterface
{
    string Get(string str= "Default");
}

class MyClass : IMyInterface
{
    public string Get(string str = "A different string!")
    {
        return str;
    }
}

And this code compiles fine. But that can't be right surely? Then with a bit more digging, I discovered this:

  IMyInterface obj = new MyClass();
  Console.WriteLine(obj.Get()); // writes "Default"

  MyClass cls = new MyClass();
  Console.WriteLine(cls.Get()); // writes "A different string!"

It would seem to be that the calling code is getting the value of the optional parameter based on the objects declared type, then passing it to the method. This, to me, seems a bit daft. Maybe optional parameters and method overloads both have their scenarios when they should be used?

My question

My calling code being passed an instance of IMyInterface and needs to call both methods here at different points.

Will I be forced to implement the same method overload in every implementation?

public string Get()
{
  return Get("Default");
}
Symbology answered 11/11, 2011 at 13:25 Comment(6)
See Eric Lippert's Optional argument corner cases, part twoBeware
+1 This tells me never to define interfaces with optional parameters. Too much ambiguity.Delaware
@GertArnold Since the question, I've found out a bit more. I think I'll post it as an answer to the question because I did actually find a solution.Symbology
Go ahead, happens all the time here. (you know, of course)Delaware
@GertArnold done :D I like to see people answer their own questions too. Hopefully other readers will benefit from the question + answer in the future.Symbology
I really don't think default parameters should be allowed on interfaces. They are an implementation detail.Khalkha
S
3

What I also didn't realise, is that optional parameters don't change the method signature. So the following code is perfectly legal and was actually my solution:

interface IMyInterface
{
    string Get(string str = "Default");
}

class MyClass : IMyInterface
{
    public string Get(string str)
    {
        return str;
    }
}

So if I have an instance of MyClass, I must call Get(string str), but if that instance has been declared as the base interface IMyInterface, I can still call Get(), which gets the default value from IMyInterface first, then invokes the method.

Symbology answered 11/11, 2011 at 16:22 Comment(1)
Consider that you could also have public string Get(string whateverIWant). Some parts of the the interface are promises (it promises to take a string and return a string), some are hints (it suggests letting the calling code leave out the parameter, and have the compiler change it to a call with "Default") and some are documentation (the name of the parameter matters not unless called by name).Mafala
C
0

Your solution didn't suit me as I wanted each child to use their own default values, using reflection, I found a way to make it work :

interface IMyInterface {
    string MyMethod() {
        BindingFlags bf = BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.OptionalParamBinding;
        this.GetType().InvokeMember("MyMethod",bf,null,this,new object[] {v,i,j,Type.Missing});
    }
    string MyMethod(string param1) { }
}

public class MyClass : IMyInterface
{
    public string MyMethod(string str = "DefaultFromClass") { 
        return str;
     }
}

The way it works, when a method is called, if it's not implemented in the class, it calls the method with one more optional parameter with Type.Missing which check if the class implements this overload of the method. This can be expanded to have more than one parameter by simply continuing to call the next one every time.

Note that I need to define a default implementation of the methods in the interface, which is a feature introduced in c# 8.0, so it was not possible at the time of your post.

Colunga answered 24/7, 2022 at 1:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.