76

Using c# 4.0 -- building an interface and a class that implements the interface. I want to declare an optional parameter in the interface and have it be reflected in the class. So, I have the following:

 public interface IFoo
 {
      void Bar(int i, int j=0);
 }

 public class Foo
 {
      void Bar(int i, int j=0) { // do stuff }
 }

This compiles, but it doesn't look right. The interface needs to have the optional parameters, because otherwise it doesn't reflect correctly in the interface method signature.

Should I skip the optional parameter and just use a nullable type? Or will this work as intended with no side effects or consequences?

Ahmad Mageed
  • 94,561
  • 19
  • 163
  • 174
bryanjonker
  • 3,386
  • 3
  • 24
  • 37
  • 2
    Martin's comments below give a detailed example of the various pitfalls, and I wish the compiler would flag non-matching default arguments. That said, I am using this in my code because it does indicate my intent as a developer at both the interface and implementation level for other developers to see. – Pete Magsig Jul 09 '14 at 15:13
  • Interesting related questions, "[Is there any reason to declare optional parameters in an interface?](https://stackoverflow.com/questions/6752762/)", and, "[Why are C# 4 optional parameters defined on interface not enforced on implementing class?](https://stackoverflow.com/questions/4922714/)", the second with an interesting [answer from Lippert](https://stackoverflow.com/a/4923642/1028230). – ruffin Sep 07 '17 at 16:55

6 Answers6

70

What is really strange is that the value you put for the optional parameter in the interface actually makes a difference. I suppose you have to question whether the value is an interface detail or an implementation detail. I would have said the latter but things behave like the former. The following code outputs 1 0 2 5 3 7 for example.

// Output:
// 1 0
// 2 5
// 3 7
namespace ScrapCSConsole
{
    using System;

    interface IMyTest
    {
        void MyTestMethod(int notOptional, int optional = 5);
    }

    interface IMyOtherTest
    {
        void MyTestMethod(int notOptional, int optional = 7);
    }

    class MyTest : IMyTest, IMyOtherTest
    {
        public void MyTestMethod(int notOptional, int optional = 0)
        {
            Console.WriteLine(string.Format("{0} {1}", notOptional, optional));
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyTest myTest1 = new MyTest();
            myTest1.MyTestMethod(1);

            IMyTest myTest2 = myTest1;
            myTest2.MyTestMethod(2);

            IMyOtherTest myTest3 = myTest1;
            myTest3.MyTestMethod(3);
        }
    }
}

What is kind of interesting is that if your interface makes a parameter optional the class implementing it does not have to do the same:

// Optput:
// 2 5
namespace ScrapCSConsole
{
    using System;

    interface IMyTest
    {
        void MyTestMethod(int notOptional, int optional = 5);
    }

    class MyTest : IMyTest
    {
        public void MyTestMethod(int notOptional, int optional)
        {
            Console.WriteLine(string.Format("{0} {1}", notOptional, optional));
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyTest myTest1 = new MyTest();
            // The following line won't compile as it does not pass a required
            // parameter.
            //myTest1.MyTestMethod(1);

            IMyTest myTest2 = myTest1;
            myTest2.MyTestMethod(2);
        }
    }
}

What seems to be a mistake however is that if you implement the interface explicitly the value you give in the class for the optional value is pointless. How in the following example could you use the value 9?

// Optput:
// 2 5
namespace ScrapCSConsole
{
    using System;

    interface IMyTest
    {
        void MyTestMethod(int notOptional, int optional = 5);
    }

    class MyTest : IMyTest
    {
        void IMyTest.MyTestMethod(int notOptional, int optional = 9)
        {
            Console.WriteLine(string.Format("{0} {1}", notOptional, optional));
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyTest myTest1 = new MyTest();
            // The following line won't compile as MyTest method is not available
            // without first casting to IMyTest
            //myTest1.MyTestMethod(1);

            IMyTest myTest2 = new MyTest();            
            myTest2.MyTestMethod(2);
        }
    }
}

Eric Lippert wrote an interesting series on this exact topic: Optional argument corner cases

Martin Brown
  • 24,692
  • 14
  • 77
  • 122
  • 3
    The default values are compiled into the call-site and that's why only the values from the type you call the method on are used. – keuleJ May 06 '21 at 08:02
30

You could consider the pre-optional-parameters alternative:

public interface IFoo
{
    void Bar(int i, int j);
}

public static class FooOptionalExtensions
{
    public static void Bar(this IFoo foo, int i)
    {
        foo.Bar(i, 0);
    }
}

If you don't like the look of a new language feature, you don't have to use it.

pdr
  • 6,372
  • 1
  • 29
  • 38
  • 1
    Feel free to provide additional explanation of how this works, or perhaps a link to what you're referring to as "pre-optional-paremeters alternative." Might help future users! :] – Danny Bullis Oct 21 '16 at 17:22
  • 1
    Of course the really old way of doing this (pre C# 3 introducing extension methods) was to use method overloading in both the class and interface. If you have access to the class code overloading is probably better than an extension method anyway as it keeps the code in one place. – Martin Brown Mar 02 '17 at 12:34
5

You don't have to make the parameter optional in the implementation. Your code will make somewhat more sense then:

 public interface IFoo
 {
      void Bar(int i, int j = 0);
 }

 public class Foo
 {
      void Bar(int i, int j) { // do stuff }
 }

This way, it's unambiguous what the default value is. In fact, I'm pretty sure the default value in the implementation will have no effect, since the interface provides a default for it.

Tor Haugen
  • 19,509
  • 9
  • 45
  • 63
  • 9
    The default value in the implementation will have an effect if your reference is typed with the class rather than interface. – Martin Brown Mar 02 '17 at 12:40
3

What about something like this?

public interface IFoo
{
    void Bar(int i, int j);
}

public static class IFooExtensions 
{
    public static void Baz(this IFoo foo, int i, int j = 0) 
    {
        foo.Bar(i, j);
    }
}

public class Foo
{
    void Bar(int i, int j) { /* do stuff */ }
}
David
  • 19,389
  • 12
  • 63
  • 87
2

The thing to consider is what happens when Mocking frameworks are used, which work based on reflection of the interface. If optional parameters are defined on the interface, default value would be passed based on what is declared in the interface. One issue is that there is nothing stopping you from setting different optional values on the definition.

Hitesh
  • 86
  • 5
0

I also can suggest this:

public interface IFoo
{
      void Bar(int i);
      void Bar(int i, int j);
}

public class Foo
{
     // when "j" has default value zero (0).
     void Bar(int i)
     {
         Bar(i, 0);
     }
     
     void Bar(int i, int j) {}
}