2010-04-02 4 views
55

Использование 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 } 
} 

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

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

+2

В комментариях Мартина ниже приводится подробный пример различных ошибок, и я хочу, чтобы компилятор указывал несоответствующие аргументы по умолчанию. Тем не менее, я использую это в своем коде, потому что это указывает на мое намерение как разработчика на уровне интерфейса и реализации для других разработчиков. –

+0

Интересные связанные вопросы: «[Есть ли какие-либо причины для объявления необязательных параметров в интерфейсе?] (Https: // stackoverflow.com/questions/6752762 /) ", и" [Почему дополнительные параметры C# 4, определенные на интерфейсе, не выполняются при реализации класса?] (https://stackoverflow.com/questions/4922714/), второй - с интересным [ответ от Lippert] (https://stackoverflow.com/a/4923642/1028230). – ruffin

ответ

28

Вы могли бы рассмотреть альтернативные предварительные факультативные-параметры:

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

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

+40

Но ... это новое! И блестящее! :-) – bryanjonker

+1

Не стесняйтесь предоставлять дополнительное объяснение того, как это работает, или, возможно, ссылка на то, что вы называете альтернативой pre-optional-paremeters. Может помочь будущим пользователям!] –

+0

Конечно, действительно старый способ сделать это (pre C# 3), чтобы использовать методы перегрузки в обоих класс и интерфейс.Если у вас есть доступ к перегрузке кода класса, вероятно, лучше, чем метод расширения, так как он сохраняет код в одном месте. –

1

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

+2

Это не дает ответа на вопрос. Критиковать или запрашивать разъяснения от автора , оставьте nt ниже их сообщения. –

+3

Я не знаю. Мне кажется, что это прямой ответ на вопрос: «Или это будет работать по назначению без каких-либо побочных эффектов или последствий?» – MojoFilter

3

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

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 */ } 
} 
+0

@Iznogood - редактирование, которое вы одобрили здесь, явно недействительное редактирование: http://stackoverflow.com/review/spected-edits/1041521. Будьте внимательны при просмотре изменений. – LittleBobbyTables

44

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

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

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

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

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

+2

Значение по умолчанию в реализации будет иметь эффект, если ваша ссылка вводится с классом, а не с интерфейсом. –

2

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

Смежные вопросы