41

I was playing with optional parameters to see how they would work with interfaces and I came across a strange warning. The setup I had was the following code:

 public interface ITestInterface
 {
     void TestOptional(int a = 5, int b = 10, object c = null);
 }

 public class TestClass : ITestInterface
 {

     void ITestInterface.TestOptional(int a = 5, int b = 5, object c = null)
     {
        Console.Write("a=" + a + " b=" + b + " c=" + c);
     }
 }

The compiler gives me the following warnings:

  • The default value specified for parameter 'a' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments
  • The default value specified for parameter 'b' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments
  • The default value specified for parameter 'c' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments

If I run this with the following code:

class Program
{
    static void Main(string[] args)
    {
        ITestInterface test = new TestClass();
        test.TestOptional();
        Console.ReadLine();
    }
}

I get the output of "a=5 b=10 c=" as I'd expect.

My question is what is warning for? What contexts is it referring to?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Craig Suchanec
  • 10,474
  • 3
  • 31
  • 39
  • Does this answer your question? [Why are C# 4 optional parameters defined on interface not enforced on implementing class?](https://stackoverflow.com/questions/4922714/why-are-c-sharp-4-optional-parameters-defined-on-interface-not-enforced-on-imple) – EJoshuaS - Stand with Ukraine Apr 23 '20 at 20:34
  • For clarity, what this question is talking about is going to be an actual thing in c# soon. https://devblogs.microsoft.com/dotnet/default-implementations-in-interfaces/ – Chris Marisic May 22 '20 at 17:34

3 Answers3

29

The problem with optional arguments in C# is whether the callee sees the object as a TestClass or an ITestInterface. In the first case, the values declared in the class apply. In the second case the values declared in the interface apply. It is because the compiler uses the statically available type information to construct the call. In case of an explicit interface implementation the method is never called 'for a class', always 'for an interface'

The C# Language Specification in 10.6.1 states:

If optional parameters occur in an implementing partial method declaration (§10.2.7) , an explicit interface member implementation (§13.4.1) or in a single-parameter indexer declaration (§10.9) the compiler should give a warning, since these members can never be invoked in a way that permits arguments to be omitted.

Ondrej Tucny
  • 27,626
  • 6
  • 70
  • 90
  • 1
    @GregC Yes, they don't have any effect in case of an explicit implementation. In case of an implicit implementation the important thing is to maintain the same values in the interface and in the classes. Otherwise you can end up with some very-hard-to-discover bugs. – Ondrej Tucny Apr 15 '11 at 23:18
  • They were pretty hard to discover in C++ as well, so C# team opted to wait with default parameter values until recently. I think I saw this on Eric L.'s blog, i think. – GregC Apr 15 '11 at 23:21
25

The compiler is telling you

void ITestInterface.TestOptional(int a = 5, int b = 5, object c = null)

Is fundamentally the same as

void ITestInterface.TestOptional(int a, int b, object c)

The reason being, since you have to invoke TestOptional through the interface the interface will supply the parameters. There is no way at the class for you to have not been supplied a parameter value.

2020 edit: soon you will be able to do this with C# 8 by default implementations of interfaces

interface ILogger
{
    void Log(LogLevel level, string message);
    void Log(Exception ex) => Log(LogLevel.Error, ex.ToString());
}

You could just as easily do something akin to:

public interface ITestInterface
{
     void TestOptional(int a, int b, object c);

     void TestOptional() => TestOptional(5);
     void TestOptional(int a) => TestOptional(a, 10)
     void TestOptional(int a, int b) => TestOptional(a, b, null);

}
Chris Marisic
  • 32,487
  • 24
  • 164
  • 258
  • 1
    I'm probably duplicating one or more of these answers, but i'm going to add this response anyway. None of the answers were really clear what they were explaining, I figured it out but I think it could be better explained with the code samples above. – Chris Marisic Apr 17 '14 at 19:38
  • 3
    Simplest explanation of all !! – Sachin Parashar May 08 '19 at 12:38
4

Craig,

These warnings are coming from the default values specified in class method implementation. In .net, the default value for the argument is always determined by the reference type. And of course, with an explicit interface implementation like this it is only possible to call this method via an interface reference - which defines the default value. As such it's of course irrelevant what value you put here in the class, since it'll never ever be resolved and you can remove it happily. Intellisense will be fine, given that the default value here can never ever be effective.

http://funcakes.posterous.com/?tag=c

http://funcakes.posterous.com/c-40-optional-parameters-default-values-and-i

Priyank
  • 10,503
  • 2
  • 27
  • 25
  • 1
    Your term "The reference type" in the second sentence is a bit unclear. It might be helpful to explain that a method definition that implicitly implements an interface effectively defines two entry points; default parameters are honored for one and ignored for the other. An explicit interface definition just defines one entry point, and default parameters are ignored there; it doesn't define any entry point where defaults would be honored. – supercat Dec 19 '13 at 16:17