Ich habe eine Klasse als solche
%Vor% Dabei wird TwoType
von OneType
abgeleitet.
Nun, ich habe diese Hilfsmethode
%Vor% Welche Funktion überprüft offensichtlich den Typ von vd
und erstellt einen geeigneten MyClass
-Typ. Das einzige Problem ist der obige Code wird nicht kompiliert, und ich weiß nicht warum
Der Fehler ist
Ausdruck von T kann nicht in TwoType
umgewandelt werden
Wie Grzenio richtig bemerkt, ist ein Ausdruck vom Typ T nicht in TwoType konvertierbar. Der Compiler weiß nicht, dass der Ausdruck garantiert vom Typ TwoType ist - das wird von Ihrer "if" -Anweisung garantiert, aber der Compiler berücksichtigt nicht die Implikationen der if-Anweisung bei der Analyse von Typen. Vielmehr nimmt der Compiler an, dass T ein beliebiger Typ sein kann, der die Einschränkung erfüllt, einschließlich ThreeType, ein Typ, der von OneType abgeleitet ist, aber nicht von TwoType. Offensichtlich gibt es keine Konvertierung von ThreeType in TwoType, und daher gibt es auch keine Konvertierung von T nach TwoType.
Sie können den Compiler dazu bringen, es zu erlauben, indem Sie sagen: "Nun, behandeln Sie das T als Objekt und dann das Objekt auf TwoType". Jeder Schritt auf dem Weg ist legal - T ist konvertierbar in Objekt, und es könnte eine Umwandlung von Objekt zu TwoType geben, so dass der Compiler es erlaubt.
Sie werden dann das gleiche Problem bekommen, wenn Sie von SubClass nach MyClass<T>
konvertieren. Auch hier können Sie das Problem lösen, indem Sie zuerst auf das Objekt werfen.
Dieser Code ist jedoch immer noch falsch:
%Vor%Warum ist es falsch? Nun, überlege alles, was hier schiefgehen könnte. Zum Beispiel: Sie sagen
%Vor%Was passiert? Wir rufen den SubClass-Konstruktor nicht auf, weil das Argument nicht genau vom Typ TwoType ist, sondern von einem Typ, der von TwoType abgeleitet ist. Anstelle eines Schalters möchten Sie wahrscheinlich
%Vor%Das ist immer noch falsch. Denken Sie wieder darüber nach, was schief gehen könnte:
%Vor% Was passiert jetzt? Das Argument ist TwoType, wir erstellen eine neue SubClass und versuchen dann, sie in MyClass<OneType>
zu konvertieren, aber es gibt keine Konvertierung von SubClass nach MyClass<OneType>
, so dass dieser zur Laufzeit abstürzt und abstürzt.
Der korrekte Code für Ihre Fabrik ist
%Vor%Ist das jetzt klar? Sie könnten einen völlig anderen Ansatz betrachten; Wenn Sie diese vielen Umwandlungen und Laufzeitprüfungen benötigen, um den Compiler davon zu überzeugen, dass der Code korrekt ist, ist dies ein Beweis dafür, dass das Ganze ein schlechter Code-Geruch ist.
Es wird nicht in .Net 3.5 und darunter funktionieren. - SubClass hat nicht den Typ MyClass<T>
für irgendein T, sondern nur vom Typ MyClass<TwoType>
. Und die generischen Klassen folgen nicht der Vererbung ihres Schablonentyps, z. MyClass<string>
ist nicht eine Unterklasse von MyClass<object>
- sie sind komplett verschiedene Klassen in C #.
Leider kenne ich keine vernünftige Möglichkeit, Ihre Fabrikmethode zu schreiben.
Ich weiß, das ist eine alte Frage, aber ich denke, es verdient auch eine Antwort auf warum das unmöglich ist (ohne verschiedene hässliche Problemumgehungen zu verwenden).
Wenn Sie Generics verwenden, arbeiten Sie gewissermaßen mit Vorlagencode. Der Compiler verwendet strengere Regeln für Ihren Code, da er auch zur Laufzeit kompilierbar sein muss, wo die endgültige Version kompiliert wird.
Wenn Sie also Klassen oder Methoden mit Generics erstellen, müssen diese für alle möglichen Kombinationen kompilierbar sein, die den Beschränkungen entsprechen, die Sie mit Ihren Einschränkungen für die generischen Parameter festgelegt haben.
Also habe ich Ihren Code noch weiter vereinfacht, um Ihnen zu zeigen, was passiert:
Ich erkläre zuerst 3 Klassen. Eine Elternklasse und zwei Kinder:
%Vor%Ich erkläre dann eine generische Methode, wo ich versuche, den Typ zu testen, und dann auf den untergeordneten Typ umwandeln:
%Vor%Dies gibt Ihnen den gleichen Fehler wie in der ursprünglichen Frage, obwohl es intuitiv wie fester Code aussieht. Was jedoch tatsächlich passiert, ist, dass der Compiler prüft, ob der Code zur Laufzeit in einer beliebigen legalen Version der Methode kompilierbar ist. Das ist, was ich damit meinte, dass Sie mit Vorlagen arbeiten, weil zur Laufzeit, wenn Sie die Methode mit "Schwester" als Ihr Typparameter aufrufen, Sie erhalten würden:
%Vor%Es ist daher meine Vermutung (ich weiß es nicht genau, aber bitte korrigieren Sie mich, wenn ich falsch liege), dass diese Einschränkung, die Sie bei der Arbeit mit Generika haben, ist, weil Ihr Code nicht brechen sollte, einfach weil Sie ihn aufrufen in einem anderen Kontext, und deshalb erlauben sie Ihnen nicht, Code wie das an erster Stelle zu schreiben, obwohl es möglicherweise keine ungültigen Kombinationen zur Kompilierzeit gibt.
Das hat mir geholfen zu verstehen, warum bestimmte Dinge getan werden können, und bestimmte Dinge nicht. Ja, Sie können "as" anstelle von typecasting verwenden, da der Compiler Ihnen nur "null" gibt, wenn die Umwandlung ungültig ist, aber bei expliziter Typisierung überprüft der Compiler, ob dies möglich ist. Für Generika ist es nicht, um sicherzustellen, dass es auch zur Laufzeit kompilieren kann.
Ändern Sie Ihre Fabrikmethode:
%Vor%Dann brauchen Sie den Schalter überhaupt nicht.
Können Sie die Designrunde umdrehen? Anstatt eine unflexible Factory-Methode zu erstellen, die über jede Unterklasse von OneType
informiert sein muss, fügen Sie eine abstrakte Methode zu OneType
hinzu, die wie folgt aussieht:
TwoType wird verantwortlich für die Erstellung von SubClass
-Objekten, ThreeType
kann SubTypeThree
usw. zurückgeben.
Der Hinweis hier ist, dass Sie einen Wechsel basierend auf dem Objekttyp durchführen. Dies ist immer ein guter Kandidat, um die Unterklassen dazu zu bringen, die Arbeit zu erledigen.
EDIT 1: Beispiel
ZB;
%Vor%Das funktioniert für mich:
%Vor%in meiner Form hatte ich das:
%Vor%EDIT: Offensichtlich ist das nicht ideal, wie Eric oben erwähnt hat. Während dieser Code technisch kompiliert und bis zu einem gewissen Grad funktioniert, möchten Sie vielleicht eine bessere Gesamtlösung finden.