Warum werden optionale C # 4-Parameter, die auf der Schnittstelle definiert sind, bei der Implementierung der Klasse nicht erzwungen?

361

Ich bemerkte , dass mit den optionalen Parameter in C # 4 , wenn Sie einen optionalen Parameter für eine Schnittstelle geben Sie don t haben , um diesen Parameter optional auf jeder implementierenden Klasse zu machen:

public interface MyInterface
{
    void TestMethod(bool flag = false);
}

public class MyClass : MyInterface
{
    public void TestMethod(bool flag)
    {
        Console.WriteLine(flag);
    }
}

und deshalb:

var obj = new MyClass();        
obj.TestMethod(); // compiler error

var obj2 = new MyClass() as MyInterface;
obj2.TestMethod(); // prints false

Weiß jemand, warum optionale Parameter so funktionieren?

Einerseits denke ich, dass die Möglichkeit, alle auf den Schnittstellen angegebenen Standardwerte zu überschreiben, nützlich ist. Um ehrlich zu sein, bin ich mir nicht sicher, ob Sie überhaupt Standardwerte auf der Schnittstelle angeben können sollten, da dies eine Implementierungsentscheidung sein sollte.

Andererseits bedeutet diese Trennung, dass Sie die konkrete Klasse und die Schnittstelle nicht immer austauschbar verwenden können. Dies wäre natürlich kein Problem, wenn der Standardwert in der Implementierung angegeben wird. Wenn Sie jedoch Ihre konkrete Klasse als Schnittstelle verfügbar machen (z. B. mithilfe eines IOC-Frameworks, um die konkrete Klasse einzufügen), gibt es wirklich keine Punkt mit dem Standardwert, da der Anrufer ihn trotzdem immer angeben muss.

theburningmonk
quelle
22
Weil sie optional sind?
Oded
1
Sie können die Objektinstanz jedoch umwandeln MyInterfaceund mit dem optionalen Parameter aufrufen : ((MyInterface)obj).TestMethod();.
Jim Mischel
7
@oded - aber wenn Sie sagen, dass dieser Parameter im Vertrag optional ist, warum erlauben Sie dem Implementierer, ihn nicht optional zu machen? Verursacht das nicht nur Verwirrung bei jemandem, der den Vertrag nutzen möchte?
Theburningmonk
1
Ich denke, in diesem Fall können Sie sagen, dass der Parameter beim Implementieren optional ist, nicht beim Aufrufen der Implementierungsmethoden. Wenn Sie die Methode in der Klasse aufrufen, müssen Sie die Klassenregeln befolgen (der Parameter ist in der Klasse nicht optional, damit Sie dies können Rufen Sie die Methode nicht ohne sie auf. Wenn Sie die Schnittstelle implementieren, müssen Sie in zweiter Hand die Schnittstellenregeln befolgen, damit Sie die Methoden mit / ohne optionale Parameter überschreiben können. Nur eine Meinung.
Mohamad Alhamoud
2
Weitere detaillierte Problemerklärung hier -> geekswithblogs.net/BlackRabbitCoder/archive/2010/06/17/…
Andrew Orsich

Antworten:

236

UPDATE: Diese Frage war das Thema meines Blogs am 12. Mai 2011. Danke für die tolle Frage!

Angenommen, Sie haben eine Schnittstelle, wie Sie sie beschreiben, und hundert Klassen, die sie implementieren. Dann entscheiden Sie sich, einen der Parameter einer der Methoden der Schnittstelle optional zu machen. Schlagen Sie vor, dass der Compiler den Entwickler zwingen muss, jede Implementierung dieser Schnittstellenmethode zu finden und den Parameter ebenfalls optional zu machen?

Angenommen, wir haben das getan. Angenommen, der Entwickler hatte nicht den Quellcode für die Implementierung:


// in metadata:
public class B 
{ 
    public void TestMethod(bool b) {}
}

// in source code
interface MyInterface 
{ 
    void TestMethod(bool b = false); 
}
class D : B, MyInterface {}
// Legal because D's base class has a public method 
// that implements the interface method

Wie soll der Autor von D diese Arbeit machen? Müssen sie in Ihrer Welt den Autor von B am Telefon anrufen und sie bitten, ihnen eine neue Version von B zu senden, mit der die Methode einen optionalen Parameter hat?

Das wird nicht fliegen. Was ist, wenn zwei Personen den Autor von B anrufen und einer von ihnen möchte, dass die Standardeinstellung wahr ist, und einer von ihnen möchte, dass sie falsch ist? Was ist, wenn der Autor von B sich einfach weigert, mitzuspielen?

Vielleicht müssten sie in diesem Fall sagen:

class D : B, MyInterface 
{
    public new void TestMethod(bool b = false)
    {
        base.TestMethod(b);
    }
}

Die vorgeschlagene Funktion scheint dem Programmierer eine Menge Unannehmlichkeiten zu bereiten, ohne dass die repräsentative Leistung entsprechend erhöht wird. Was ist der überzeugende Vorteil dieser Funktion, der die erhöhten Kosten für den Benutzer rechtfertigt?


UPDATE: In den Kommentaren unten schlägt supercat eine Sprachfunktion vor, die der Sprache wirklich mehr Leistung verleiht und einige Szenarien ermöglicht, die den in dieser Frage beschriebenen ähnlich sind. Zu Ihrer Information, diese Funktion - Standardimplementierungen von Methoden in Schnittstellen - wird zu C # 8 hinzugefügt.

Eric Lippert
quelle
9
@supercat: Wir haben keine Pläne dazu, aber eine solche Funktion ist möglich. Es wurde schon früher vorgeschlagen. Während des Entwurfsprozesses für C # 4 haben wir viele der so genannten "Erweiterung alles" -Funktionen umgangen - wir haben Erweiterungsmethoden. Was würde es also bedeuten, Erweiterungsereignisse, Erweiterungseigenschaften, Erweiterungskonstruktoren, Erweiterungsschnittstellen usw. zu haben? . Klassen, die Sie an Schnittstellen "anhängen" und sagen können, "diese Methoden sind die Standardimplementierungen dieser Schnittstelle", sind eine Möglichkeit, "Erweiterungsschnittstellen" zu charakterisieren. Eine interessante Idee.
Eric Lippert
16
Um meine Argumentation zu klären, warum ich es für nicht intuitiv halte: Für mich ist ein optionaler Parameter "optional für den Aufrufer der Methode" NICHT "optional für den Implementierer der Schnittstelle".
Stefan de Kok
8
"Müssen sie in Ihrer Welt den Autor von B am Telefon anrufen und sie bitten, ihnen eine neue Version [...] zu schicken?" Nein, keine Anrufe erforderlich. B sollte einfach nicht länger als Implementierung von MyInterface angesehen werden können, und der Autor von D könnte vom Compiler darauf aufmerksam gemacht werden. Der Autor von D müsste D mit einer Testmethode implementieren, die einen Standardparameter akzeptiert, da dies vom Schnittstellenautor verlangt wird. Sie sprechen sich dagegen aus, dem Schnittstellenautor zu erlauben, eine Einschränkung durchzusetzen, weil jemand sie möglicherweise aufheben möchte. Das ist kein gutes Argument.
Philofinfinitejest
7
@EricLippert In dem Fall, in dem der optionale Parameter zur Vereinfachung der Codierung angegeben wird, ist das Erzwingen von Unannehmlichkeiten bei der Implementierung nicht intuitiv. In dem Fall, in dem der optionale Parameter verwendet wird, um die Überladung von Methoden, eine leistungsstarke Funktion, zu minimieren, können diese optionalen Werte eine große Bedeutung erlangen, und wenn sie ignoriert werden, wird diese Leistung sicherlich verringert. Ganz zu schweigen von dem Fall, in dem die konkrete Implementierung einen anderen Standardwert als die Schnittstelle angibt. Es scheint nur eine sehr merkwürdige Trennung zu sein, um dies zuzulassen.
Philofinfinitejest
8
Ich stimme hier mit @philofinfinitejest überein. Eine Schnittstelle sagt, was etwas tun kann. Wenn mir eine Implementierung einer Schnittstelle mit einem anderen Standardwert als dem von der Schnittstelle angegebenen übergeben wird, woher soll ich das wissen? Hey, ich habe eine Schnittstelle, die standardmäßig true übergibt. Warum wird dieses Ding also falsch? Das scheint, als hätte ich NICHT die Schnittstelle bekommen, die ich erwartet hatte. Schlimmer noch, ich muss jetzt auf eine Implementierung und nicht auf eine Schnittstelle programmieren.
Aaronburro
47

Ein optionaler Parameter wird nur mit einem Attribut versehen. Dieses Attribut weist den Compiler an, den Standardwert für diesen Parameter an der Aufrufstelle einzufügen.

Der Aufruf obj2.TestMethod();wird ersetzt durch, obj2.TestMethod(false);wenn der C # -Code in IL kompiliert wird und nicht zur JIT-Zeit.

In gewisser Weise ist es immer der Aufrufer, der den Standardwert mit optionalen Parametern bereitstellt. Dies hat auch Konsequenzen für die binäre Versionierung: Wenn Sie den Standardwert ändern, den aufrufenden Code jedoch nicht neu kompilieren, wird weiterhin der alte Standardwert verwendet.

Andererseits bedeutet diese Trennung, dass Sie die konkrete Klasse und die Schnittstelle nicht immer austauschbar verwenden können.

Sie können das bereits nicht tun, wenn die Schnittstellenmethode war explizit implementiert wurde .

CodesInChaos
quelle
1
Können Sie Implementierer zwingen, explizit zu implementieren?
Crush
30

Da Standardparameter zur Kompilierungszeit und nicht zur Laufzeit aufgelöst werden. Die Standardwerte gehören also nicht zu dem aufgerufenen Objekt, sondern zu dem Referenztyp, über den es aufgerufen wird.

Olhovsky
quelle
7

Optionale Parameter sind nach meinem Verständnis eine Art Makrosubstitution. Sie sind aus Sicht der Methode nicht wirklich optional. Ein Artefakt davon ist das Verhalten, bei dem Sie unterschiedliche Ergebnisse erhalten, wenn Sie auf eine Schnittstelle übertragen.

Ariel Arjona
quelle