2010-04-02 23 views
55

Korzystanie z C# 4.0 - tworzenie interfejsu i klasy implementującej interfejs. Chcę zadeklarować opcjonalny parametr w interfejsie i umieścić go w klasie. Tak, mam następujące:Parametry opcjonalne dla interfejsów

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

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

To kompiluje, ale nie wygląda dobrze. Interfejs musi mieć opcjonalne parametry, ponieważ w przeciwnym razie nie odzwierciedla poprawnie w sygnaturze metody interfejsu.

Czy powinienem pominąć opcjonalny parametr i po prostu użyć typu zerowego? A może to działa zgodnie z przeznaczeniem bez żadnych skutków ubocznych lub konsekwencji?

+2

Poniższe komentarze Martina zawierają szczegółowy przykład różnych pułapek i chciałbym, aby kompilator oznaczał niezgodne domyślne argumenty. To powiedziawszy, używam tego w moim kodzie, ponieważ wskazuje on mój zamiar jako programisty zarówno na poziomie interfejsu, jak i na poziomie implementacji, aby inni programiści mogli to zobaczyć. –

+0

Interesujące pytania pokrewne: "[Czy jest jakikolwiek powód deklarowania opcjonalnych parametrów w interfejsie?] (Https: // stackoverflow.com/questions/6752762 /) "i" [Dlaczego parametry opcjonalne C# 4 zdefiniowane w interfejsie nie są wymuszane w klasie implementacji?] (https://stackoverflow.com/questions/4922714/) ", drugie z interesującym [odpowiedź od Lipperta] (https://stackoverflow.com/a/4923642/1028230). – ruffin

Odpowiedz

28

można rozważyć alternatywę-pre-opcjonalne parametry:

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

Jeśli nie podoba Ci się wygląd nowej funkcji języka, nie trzeba go używać.

+40

Ale ... to jest nowe! I lśniące! :-) – bryanjonker

+1

Możesz podać dodatkowe wyjaśnienie, jak to działa, a może link do tego, co nazywasz "alternatywną alternatywą paremetrów". Może pomóc przyszłym użytkownikom!:] –

+0

Oczywiście, naprawdę starym sposobem na to (pre C# 3 wprowadzenie metod rozszerzenia) było użycie przeciążania metod w obu klasa i interfejs. Jeśli masz dostęp do przeciążania kodu klasy, to prawdopodobnie i tak jest lepszy niż metoda rozszerzenia, ponieważ utrzymuje kod w jednym miejscu. –

1

Wygląda na to, że zrobi dokładnie to, co brzmi, jak chcesz osiągnąć.

+2

To nie zapewnia odpowiedzi na pytanie.Jak krytykować lub poprosić o wyjaśnienie autora zostawić comme nt pod swoim postem. –

+3

Nie wiem. Wydaje mi się, że jest to bezpośrednia odpowiedź na zadane pytanie: "Czy może to działać tak, jak zamierzano, bez skutków ubocznych i konsekwencji?" – MojoFilter

3

Co powiesz na coś takiego?

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 - edytowana tutaj definicja nie jest poprawna: http://stackoverflow.com/review/suggested-edits/1041521. Zachowaj ostrożność podczas sprawdzania zmian. – LittleBobbyTables

44

Co jest naprawdę dziwne jest to, że wartość można umieścić za pomocą opcjonalnego parametru w interfejsie faktycznie robi różnicę. Przypuszczam, że musisz zapytać, czy wartość jest szczegółem interfejsu, czy szczegółem implementacji. Powiedziałbym to drugie, ale rzeczy zachowują się jak poprzednie. Poniższy kod wyprowadza na przykład 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); 
     } 
    } 
} 

Jaki jest rodzaj interesujące jest to, że jeśli interfejs sprawia, że ​​parametr opcjonalny klasy wykonawczych nie musi zrobić to samo:

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

Co wydaje się być błędem jest jednak, że jeśli Interfejs jest jawnie implementowany, wartość podana w klasie dla wartości opcjonalnej jest bezcelowa. Jak w poniższym przykładzie można użyć wartości 9? To nawet nie daje ostrzeżenia podczas kompilacji.

// 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

Nie trzeba wprowadzać opcjonalnego parametru do implementacji. Twój kod będzie miał trochę więcej sensu niż:

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

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

W ten sposób jest jednoznaczne, jaka jest wartość domyślna. W rzeczywistości jestem pewien, że wartość domyślna w implementacji nie będzie miała żadnego efektu, ponieważ interfejs zapewnia dla niej domyślną wartość.

+2

Wartość domyślna w implementacji będzie działać, jeśli odwołanie zostanie wpisane za pomocą klasy, a nie interfejsu. –

2

Należy wziąć pod uwagę to, co się dzieje, gdy używane są makiety, które działają w oparciu o odbicie interfejsu. Jeśli w interfejsie zdefiniowano opcjonalne parametry, wartość domyślna zostanie przekazana w oparciu o deklarację w interfejsie. Jedną z kwestii jest to, że nic nie stoi na przeszkodzie, aby ustawić różne opcjonalne wartości w definicji.

Powiązane problemy