folgendes covariant generische SchnittstelleKovariante Schnittstelle mehrmals implementieren: Ist dieses Verhalten richtig definiert?
public interface IContainer<out T>
{
T Value { get; }
}
Da können wir eine Klasse erstellen, die diese Schnittstelle mehrmals für mehrere generische Typen implementiert. In dem Szenario, das mich interessiert, teilen diese generischen Typen einen gemeinsamen Basistyp.
public interface IPrint
{
void Print();
}
public class PrintA : IPrint
{
public void Print()
{
Console.WriteLine("A");
}
}
public class PrintB : IPrint
{
public void Print()
{
Console.WriteLine("B");
}
}
public class SuperContainer : IContainer<PrintA>, IContainer<PrintB>
{
PrintA IContainer<PrintA>.Value => new PrintA();
PrintB IContainer<PrintB>.Value => new PrintB();
}
Nun wird es interessant, wenn diese Klasse durch eine Referenz des Typs mit IContainer<IPrint>
.
Dies kompiliert und läuft ohne Problem und druckt "A". Was ich in den spec gefunden:
Die Implementierung eines bestimmten Schnittstelle Mitglieds IM, wo ich die Schnittstelle in der das Element M deklariert ist, durch bestimmt wird jede Klasse oder Struktur S Prüfung starten mit C und jede nachfolgenden Basisklasse von C wiederholt wird, bis eine Übereinstimmung befindet:
- Wenn S eine Erklärung einer explizites Schnittstellenelement Implementierung enthält , die I und M übereinstimmt, dann ist dieses Element die Umsetzung von IM
- Andernfalls, wenn S eine Deklaration eines nicht-statischen öffentlichen Mitglied enthält die M übereinstimmt, dann dieses Mitglied ist die Implementierung von IM
Der erste Aufzählungspunkt erscheint relevant zu sein, denn die Schnittstelle Implementierungen sind explizit. Es sagt jedoch nichts darüber aus, welche Implementierung ausgewählt wird, wenn mehrere Kandidaten vorhanden sind.
Es wird noch interessanter, wenn wir eine öffentliche poperty für die IContainer<PrintA>
Implementierung verwenden:
public class SuperContainer : IContainer<PrintA>, IContainer<PrintB>
{
public PrintA Value => new PrintA();
PrintB IContainer<PrintB>.Value => new PrintB();
}
nun nach oben spec, weil es eine explizite Schnittstellenimplementierung durch IContainer<PrintB>
ist, würde ich dies zu drucken erwarten "B". Es verwendet jedoch stattdessen die öffentliche Eigenschaft und druckt immer noch "A".
Ähnlich, wenn ich stattdessen IContainer<PrintA>
explizit und IContainer<PrintB>
durch öffentliche Eigenschaft implementieren, druckt es immer noch "A".
Es scheint, dass das einzige, von dem die Ausgabe abhängt, die Reihenfolge ist, in der die Schnittstellen deklariert werden. Wenn ich die Deklaration zu
public class SuperContainer : IContainer<PrintB>, IContainer<PrintA>
ändern, druckt alles "B"!
Welcher Teil der Spezifikation definiert dieses Verhalten, wenn es überhaupt richtig definiert ist?
Ich würde denken, dass es irgendwo angegeben ist, aber ich sehe es nicht. Eric Lippert hatte eine [bleg] (https://blogs.msdn.microsoft.com/ericlippert/2007/11/09/covariance-and-contravariance-in-c-part-ten-deling-with-ambiguity/) a Vor langer Zeit dachte jemand über dieses Problem nach. –