Wenn ich zwei nicht-generic Type
Objekte a
und b
, kann ich leicht überprüfen, ob ein von b unter Verwendung der a.IsAssignableFrom(b)
Funktion zugeordnet werden kann.
Nun sage ich, ich habe zwei generische Schnittstellen:
%Vor%Wenn ich sie schließe, kann ich dasselbe mit genau dem gleichen Verhalten tun, z. B.
%Vor% In der Tat, jedes T
, die verwendet werden können, schließen IDerived
kann auch aus IBase
(für das jeweilige T) zu schließen IBase<T>
und IDerived<T>
ist belegbar verwendet werden.
Allerdings
%Vor% Ich habe eine Idee, warum das so sein könnte (sie können auf verschiedenen Arten geschlossen werden und somit inkonvertierbar werden?). Ich verstehe, dass eine Funktion, die in diesem Fall wahr ist, etwas anders ist. Die Frage ist: "Ist IBase<T>
zuweisbar von IDerived<T>
für jede gültige T
?" (Danke an hvd )
Ich dachte daran, die Generika zu schließen und dann zu fragen, ob sie zuweisbar sind. Aber um allgemein zu sein, müsste ich schließen unter den allgemeinsten Typ (en) b
kann nehmen und das könnte a) hässlich sein, b) ziemlich schwer zu tun.
Ein anderer Ansatz besteht darin, die Implementierungs- / Vererbungsstruktur in b
hochzugehen und sie mit a
zu vergleichen.
Meine Frage ist, ob es einen einfacheren Weg gibt, dies auch in allgemeinen Fällen zu tun.
Motivation: allgemeines Interesse, da ich das am Ende eigentlich nicht brauche. Der ursprüngliche Bedarf dafür kam jedoch, als ich Ninject mit offenen und geschlossenen Generika verwendete und ich musste auflösen, ob eine offene generische Klasse in eine offene generische Schnittstelle (Klasse) umgewandelt werden kann.
Wie Sie bereits herausgefunden haben, wird typeof(IBase<>).IsAssignableFrom(typeof(IDerived<>))
niemals True zurückgeben, da die beiden offenen generischen Typen nicht in den jeweils anderen Vererbungshierarchien enthalten sind.
Meine Frage ist, ob es einen einfacheren Weg gibt, dies auch in allgemeinen Fällen zu tun.
Nein, nicht einfacher , aber ...
Wenn T
hat keine Einschränkungen ( where T: ...
) für eine der beiden generischen Typen Sie Zuordenbarkeit sind die Überprüfung für Ich glaube, Sie könnten in der Lage sein, die geschlossenen generischen Typen zu konstruieren, indem Sie object
als Typ Parameter und dann IsAssignableFrom
für die konstruierten Typen verwenden.
Wenn T
auf einen der generischen Typen beschränkt ist, müssen Sie Reflektion verwenden, um diese Zwänge ( Type.GetGenericArguments , Type.GetGenericParameterConstraints ) und dann die generischen Typen mit diesen Informationen konstruieren. In diesem Szenario müssen die Zwangstypen immer noch die gleiche sein, weil die A<T> : B<T>
Vererbung (das gleiche T
), um die Möglichkeit der Zuordenbarkeit zwischen den beiden generischen Typen zu haben. Beachten Sie, dass Sie, wenn ein Constraint-Typ den anderen erbt, die Zuweisbarkeit der generischen Typen finden, wenn Sie beide mit dem am meisten abgeleiteten der beiden Constraining-Typen konstruieren.
Hier sind einige Beispiele:
%Vor%Das von Ihnen bereitgestellte Beispiel einer generischen Schnittstelle, die direkt von einer anderen generischen Instanz stammt, maskiert die Komplexität bei der Lösung der Kompatibilität zwischen offenen Generika. Lesen Sie dazu den letzten Blogeintrag von Eric Lippert: Warum schließen Sie nicht automatisch Einschränkungen ein? .
In dem verlinkten Artikel werde ich auf Erics Notizen verzichten und nicht versuchen, den allgemeinen Fall zu lösen, in dem festgestellt wird, ob generische Interfaces in allen Fällen voneinander zuweisbar sind. Ein wesentlicher Teil dieser Lösung würde erfordern, zu bestimmen, ob sich die Beschränkungen (falls vorhanden) der beiden Typen überhaupt überschneiden. Sie müssten auch entscheiden, was Sie wollen Ihre hypothetische Methode zurückgeben, wenn eine offene generische Schnittstelle in einigen Fällen der anderen zuweisbar ist, aber nicht bei anderen, was passieren würde, wenn es überlappende, aber nicht zufällige Bedingungen gibt.
Aktualisiert
Zum Vergleichen der direkten Vererbung "auf den Baum gehen", wie Sie vorschlagen, ist es ziemlich einfach, wenn Sie es in einer Erweiterungsmethode verpacken. Um jedoch festzustellen, ob zwei offene generische Typen gleich sind, müssen Sie einen eigenen Vergleich definieren, da der integrierte Gleichheitsvergleich nicht mit den Typdefinitionen arbeitet, die von GetInterfaces
oder BaseType
aufgerufen werden, die für einen generischen Typ aufgerufen werden:
Dies ist wahrscheinlich darauf zurückzuführen, dass offene generische Typen, die von BaseType
oder GetInterfaces()
abgerufen wurden, null FullName
-Eigenschaften aufweisen, obwohl die Eigenschaften Namespace
und Name
aufgefüllt sind. Daher wurde ich auch von der eigenen GetFullName()
-Erweiterungsmethode definiert, mit einem optionalen Parameter strongName
, um festzulegen, ob der vollständige Assemblyname eingeschlossen werden soll.
Also, hier ist eine ziemlich kompakte Implementierung zum Vergleichen direkter Inheritance oder Implementierung zwischen offenen generischen Typen:
%Vor%Gegeben die folgenden allgemeinen Schnittstellen:
%Vor% Alle folgenden Elemente geben true
zurück:
Welches ist das inituitive identische Ergebnis wie folgt mit konstruierten generischen Typen und dem eingebauten IsAssignableFrom:
%Vor%Eine alte Frage, aber da ich bei einer Suche auf sie gestoßen bin und eine bessere Antwort (IMHO) hatte, werde ich das hier einfach weglassen ...
Die Eigenschaft BaseType
für einen offenen generischen Typ, der von GetGenericTypeDefinition
zurückgegeben wird, ist aus den von Joshua angegebenen Gründen nicht vergleichbar.
Die Eigenschaft BaseType
des geschlossenen generischen Typs gibt jedoch einen geschlossenen generischen Typ der Basis zurück. Sie können also den Baum der geschlossenen Generika aufsuchen, bis einer die Übereinstimmung open GetGenericTypeDefinition
hat.