Необязательные параметры для интерфейсов - PullRequest
64 голосов
/ 02 апреля 2010

Использование c # 4.0 - создание интерфейса и класса, который реализует интерфейс. Я хочу объявить необязательный параметр в интерфейсе и отразить его в классе. Итак, у меня есть следующее:

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

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

Это компилируется, но выглядит неправильно. Интерфейс должен иметь необязательные параметры, потому что в противном случае он не будет правильно отражаться в сигнатуре метода интерфейса.

Должен ли я пропустить необязательный параметр и просто использовать обнуляемый тип? Или это будет работать так, как задумано, без побочных эффектов или последствий?

Ответы [ 6 ]

57 голосов
/ 19 декабря 2013

Что действительно странно, так это то, что значение, которое вы указываете для необязательного параметра в интерфейсе, действительно имеет значение. Я полагаю, у вас есть вопрос, является ли значение деталью интерфейса или реализацией. Я бы сказал последнее, но все ведет себя как первый. Например, следующий код выводит 1 0 2 5 3 7.

// 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);
        }
    }
}

Что интересно, если ваш интерфейс делает параметр необязательным, то класс, реализующий его, не должен делать то же самое:

// 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);
        }
    }
}

Что кажется ошибкой, так это то, что если вы реализуете интерфейс явно, значение, которое вы задаете в классе для необязательного значения, будет бессмысленным. Как в следующем примере вы можете использовать значение 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);
        }
    }
}

Эрик Липперт написал интересную серию на эту тему: Необязательные аргументы в угловых случаях

28 голосов
/ 02 апреля 2010

Вы можете рассмотреть альтернативу предварительных необязательных параметров:

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);
    }
}

Если вам не нравится внешний вид новой языковой функции, вам не нужно ее использовать.

4 голосов
/ 31 июля 2014

Вам не нужно делать параметр необязательным в реализации. Ваш код будет иметь больше смысла, чем:

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

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

Таким образом, однозначно указано значение по умолчанию. На самом деле, я уверен, что значение по умолчанию в реализации не будет иметь никакого эффекта, так как интерфейс предоставляет для него значение по умолчанию.

3 голосов
/ 19 апреля 2012

Как насчет этого?

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 */ }
}
2 голосов
/ 14 ноября 2014

Необходимо учитывать, что происходит при использовании Mocking frameworks, которые работают на основе отражения интерфейса. Если в интерфейсе определены необязательные параметры, значение по умолчанию будет передаваться на основе того, что объявлено в интерфейсе. Одна проблема заключается в том, что ничто не мешает вам установить различные дополнительные значения в определении.

1 голос
/ 06 апреля 2010

Смотрит в сторону, это будет делать именно то, что звучит так, как вы хотите достичь.

...