2010-04-02 19 views
55

Verwenden von C# 4.0 - Erstellen einer Schnittstelle und einer Klasse, die die Schnittstelle implementiert. Ich möchte einen optionalen Parameter in der Schnittstelle deklarieren und in der Klasse spiegeln lassen. Also, habe ich folgendes:Optionale Parameter für Schnittstellen

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

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

Dies kompiliert, aber es sieht nicht richtig aus. Die Schnittstelle benötigt die optionalen Parameter, da sie sonst in der Interface-Methodensignatur nicht korrekt reflektiert wird.

Sollte ich den optionalen Parameter überspringen und nur einen NULL-fähigen Typ verwenden? Oder wird dies wie beabsichtigt ohne Nebenwirkungen oder Konsequenzen funktionieren?

+2

Martins Kommentare unten geben ein detailliertes Beispiel für die verschiedenen Fallstricke, und ich wünschte, der Compiler würde nicht übereinstimmende Standardargumente kennzeichnen. Das heißt, ich verwende das in meinem Code, weil es meine Absicht als Entwickler sowohl auf der Interface- als auch auf der Implementierungsebene für andere Entwickler anzeigt. –

+0

Interessante verwandte Fragen: "Gibt es einen Grund, optionale Parameter in einer Schnittstelle zu deklarieren?" (Https: // stackoverflow.com/questions/6752762 /) ", und" [Warum sind C# 4 optionale Parameter auf der Schnittstelle nicht am implementierenden Klasse definiert?] (https://stackoverflow.com/questions/4922714/) ", der zweite mit einem interessanten [Antwort von Lippert] (https://stackoverflow.com/a/4923642/1028230). – ruffin

Antwort

28

Sie konnten die Pre-optional-Parameter Alternative betrachten:

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

Wenn Sie das Aussehen einer neuen Sprache-Funktion nicht gefällt, müssen Sie sie nicht benutzen.

+40

Aber ... es ist neu! Und glänzend! :-) – bryanjonker

+1

Fühlen Sie sich frei, zusätzliche Erklärung dafür, wie das funktioniert, oder vielleicht ein Link zu dem, was Sie als "Pre-optional-Parameters Alternative" bezeichnen. Könnte zukünftige Benutzer helfen!:] –

+0

Natürlich war die wirklich alte Art, dies zu tun (vor C# 3 Einführung Erweiterungsmethoden), Methode Überladung in beiden zu verwenden Die Klasse und die Schnittstelle Wenn Sie Zugriff auf den Klassencode haben, ist das Überladen wahrscheinlich besser als eine Erweiterungsmethode, da der Code an einer Stelle bleibt –

1

Sieht beiseite, das wird genau das tun, was es klingt wie Sie erreichen möchten.

+2

Dies stellt keine Antwort auf die Frage dar. Kritik oder Klärung von einem Autor anfordern lass ein comme nt unter ihrem Posten. –

+3

Ich weiß es nicht. Es scheint mir, als wäre das eine direkte Antwort auf die gestellte Frage: "Oder wird das wie beabsichtigt funktionieren, ohne Nebenwirkungen oder Konsequenzen?" – MojoFilter

3

Was ist mit so etwas?

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 - die Bearbeitung, die Sie hier genehmigt haben, ist eindeutig keine gültige Bearbeitung: http://stackoverflow.com/review/suggested-edits/1041521. Bitte seien Sie vorsichtiger beim Überprüfen von Änderungen. – LittleBobbyTables

44

Was ist wirklich seltsam ist, dass der Wert, den Sie für den optionalen Parameter in der Schnittstelle setzen tatsächlich macht einen Unterschied. Ich nehme an, Sie müssen in Frage stellen, ob der Wert ein Interface-Detail oder ein Implementierungsdetail ist. Ich hätte letzteres gesagt, aber die Dinge verhalten sich wie das erste. Der folgende Code gibt zum Beispiel 1 0 2 5 3 7 aus.

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

Welche interessant ist, ist, dass, wenn Ihre Schnittstelle einen Parameter optional die Klasse macht die Umsetzung nicht das gleiche zu tun hat:

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

Was jedoch ein Fehler zu sein scheint, ist, dass, wenn Sie implementieren die Schnittstelle explizit Der Wert, den Sie in der Klasse für den optionalen Wert angeben, ist sinnlos. Wie könnten Sie im folgenden Beispiel den Wert 9 verwenden? Dies gibt nicht einmal eine Warnung beim Kompilieren.

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

Sie müssen den Parameter in der Implementierung nicht optional machen. Ihr Code wird etwas mehr Sinn dann:

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

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

Auf diese Weise ist es eindeutig, was der Standardwert ist. Ich bin mir ziemlich sicher, dass der Standardwert in der Implementierung keine Auswirkungen hat, da die Schnittstelle einen Standard dafür bietet.

+2

Der Standardwert in der Implementierung wirkt sich aus, wenn Ihre Referenz mit der Klasse und nicht mit der Schnittstelle eingegeben wird. –

2

Was zu beachten ist, passiert, wenn Mocking-Frameworks verwendet werden, die auf der Reflexion der Schnittstelle basieren. Wenn optionale Parameter in der Schnittstelle definiert sind, wird der Standardwert basierend auf den Angaben in der Schnittstelle übergeben. Ein Problem besteht darin, dass Sie nichts daran hindern können, unterschiedliche optionale Werte für die Definition festzulegen.

Verwandte Themen