Wie Sie richtig gegossen, eine Klasse zu einer abstrakten Klasse bei Verwendung von Typ-Generika?
Habe ich die folgenden Klassen
public abstract class BaseViewPresenter { }
public abstract class BaseView<T> : UserControl
where T : BaseViewPresenter { }
public class LoginPresenter : BaseViewPresenter { }
public partial class LoginView : BaseView<LoginPresenter> { }
Habe ich eine Methode, die so aussieht (vereinfacht)
public BaseView<BaseViewPresenter> Resolve(BaseViewPresenter model)
{
var type = model.GetType();
var viewType = _dataTemplates[type];
//Correctly creates BaseView object
var control = Activator.CreateInstance(viewType);
//Fails to cast as BaseView<BaseViewPresenter> so returns null
return control as BaseView<BaseViewPresenter>;
}
Wenn ich diese mit einem Instanzen von LoginPresenter
var login = new LoginPresenter();
var ctl = Resolve(login);
Die Linie Activator.CreateInstance(viewType)
korrekt aufgelöst wird in eine neue Instanzen meiner LoginView
jedoch control as BaseView<BaseViewPresenter>
nicht tun können, die cast richtig, so gibt null
.
Ist es ein Weg, um richtig wandeln Sie die control
in BaseView<BaseViewPresenter>
ohne Verwendung bestimmte Art Generika?
Seit LoginView
erbt von BaseView<LoginPresenter>
, und LoginPresenter
erbt von BaseViewPresenter
, ich würde annehmen, es gibt einen Weg, um zu konvertieren LoginView
zu BaseView<BaseViewPresenter>
.
Ich bin stecken mit .Net 3.5
T
als kovariante? Ansonsten ist diese Art von Besetzung ist nicht erlaubt.Nein, ich kann nicht, ich bin stecken mit .Net 3.5
Sie können nicht richtig gegossen, die Kontrolle in
BaseView<BaseViewPresenter>
weil es ein BaseView<LoginPresenter>
Sie könnten in der Lage sein, um Weg mit ihm: stackoverflow.com/questions/9210483/covariance-also-in-3-5-2-0 Ist mit einem 4.0-compiler erlaubt?
Aber
LoginPresenter
ist ein BaseViewPresenter
, so würde ich davon ausgehen, es ist ein Weg, dies zu erreichen-Konvertierung. Bin ich falsch in diesem?InformationsquelleAutor Rachel | 2014-09-11
Du musst angemeldet sein, um einen Kommentar abzugeben.
Dies ist eine sehr Häufig gestellte Frage. Let ' s umbenennen-Typen:
Ihre Frage ist jetzt:
Nein, ist es nicht. Sie können eine Banane in einer Schüssel mit Obst, aber Sie können nicht setzen Sie eine Banane in eine Schüssel äpfel, und deshalb eine Schale mit äpfeln und nicht eine Schale mit Obst. (Und durch ähnliches argument, eine Schüssel mit Obst ist nicht eine Schale mit äpfeln.) Da die Operationen, die Sie rechtlich kann führen Sie auf, die beiden Typen sind verschiedenen, Sie können nicht kompatibel.
Hier ist ein Foto von StackOverflow-Legende Jon Skeet Demonstration dieser Tatsache:
Die Funktion, die Sie wollen, ist aufgerufen Kontravarianz generischer, und es wird nur unterstützt, auf Schnittstellen und Delegat-Typen, wenn der compiler beweisen kann, dass die Varianz ist sicher, und wenn der Variation-Typ ein Verweistyp ist. Zum Beispiel, können Sie eine
IEnumerable<Apple>
in einem Kontext, woIEnumerable<Fruit>
ist erforderlich, da der compiler kann überprüfen, dass es keine Möglichkeit gibt, kann man sich dieBanana
in eine Sequenz von Obst.Tun, eine Suche nach "C# Kovarianz und Kontravarianz" auf dieser Website oder auf der web und finden Sie viele weitere details dazu, wie dieses feature funktioniert. Insbesondere meine Serie von Artikeln, wie wir entworfen und implementiert diese Funktion in C# 4 beginnt hier: http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx
Auf der anderen Seite, Jon tatsächlich tun können eine demonstration mit Obst (siehe Foto) und ich kann nicht einfach so tun, mit Giraffen.
oder vielleicht gibt es eine Teilmenge der baseview Vertrag (1) ist, was Sie tatsächlich benötigen vom Objekt zurückgegeben, und (2) nicht generisch sein, während einige detail nicht muss generisch sein. Dann können Sie ziehen diejenigen Mitglieder, die bis in eine Basisklasse oder Schnittstelle, und verwenden Sie diesen Typ als Rückgabe-Typ zu Lösen. Die mehr abgeleiteten generischen Typs kann eine Referenz auf die spezifische Art der Ansicht präsentieren, ohne notwendigerweise ausgesetzt wird, die Verbraucher von der Methode.
in diesem Fall könnten Sie in der Lage zu kommen mit etwas klug, aber es wäre wahrscheinlich am besten Fragen Sie in einer separaten Frage. Aber, wenn ich waren auf der Suche für eine übung im Bereich der Generika (was ich oft bin), wäre ich sehr wahrscheinlich einfach zu casten ist der DataContext in die Eigenschaft setter-oder ein-Satz-Methode-Sie sind bereits das umwandeln des Objekts zurückgegeben, die von Aktivator.CreateInstance. Casts sind ziemlich Billig-es ist nur eine Art check, wirklich.
Ich habe gerade diese heute und es ist meine neue Lieblings-Antwort auf SO.
InformationsquelleAutor Eric Lippert
Nahm ich Eric ' s Antwort da es bietet eine große Erklärung, warum das, was ich wollte, war nicht möglich, aber ich dachte auch, ich würde teilen meine Lösung falls jemand anderes läuft in das gleiche problem.
Entfernte ich den generischen Typ-parameter von meinem original
BaseView
Klasse, und erstellt eine 2. version derBaseView
Klasse, die den generischen Typ-parameter und Besonderheiten für Sie.Ist die erste version von meiner
.Resolve()
- Methode oder anderen code, der kümmert sich nicht um die spezifischen Arten und die zweite version wird von jedem code, den es interessiert, wie die Implementierung einesBaseView
Hier ist ein Beispiel, wie mein code landete auf der Suche
Das wird nicht funktionieren, weil ich nicht wollen, zu haben, zu geben eine bestimmte Art zu verwenden, die
.Resolve
Methode. Deshalb meine Frage gibt "ohne Verwendung von spezifischer Typ-Generika". Danke aber 🙂Wenn Sie es in der gleichen Weise wie in deiner Frage (var login = new LoginPresenter; var Strg = LoginPresenter.Resolve();) dann wäre es ja korrekt und zurückgeben BaseView<LoginPresenter> - keine generische Spezifikation notwendig.
Ja, ich bin traurig über das code-Beispiel, ich habe es der Einfachheit halber. In Wirklichkeit, meine
.Resolve()
Methode wird aufgerufen, aus dem code-behind einer benutzerdefiniertenUserControl
, und die Moderatorin ging in es ist dynamisch und unbekannt durch die UserControl an der Zeit, die es evalutes. Alle, die es interessiert, ist es vom TypBaseViewPresenter
perfekt, vielen Dank für die Bereitstellung eine gute Antwort
InformationsquelleAutor Rachel
Sie erwarten sind, behandeln die Art als kovariante mit Bezug auf das generische argument. Klassen können nie kovariante; Sie müssten über eine Schnittstelle, die statt (oder zusätzlich zu) einer abstrakten Klasse zu machen, kovariante mit Bezug auf
T
. Sie würden auch brauchen, um mit C# 4.0.LoginView
tut Erben vonBaseView<LoginPresenter>
, undLoginPresenter
tut Erben vonBaseViewPresenter
, so in der Theorie würde ich davon ausgehen, dass es möglich, die castLoginView
zuBaseView<BaseViewPresenter>
. Du sagst, dass ist nicht möglich, mit 3,5 obwohl?Und diese Annahme wäre falsch. Der Typ hätte sein müssen, kovariante mit Bezug auf seine generisches argument für, die, um gültig zu sein. Klassen in C# sind niemals kovariante. Schnittstellen haben die mögliche werden kovariante. Die syntax zur Unterstützung von Kovarianz für interfaces wurde Hinzugefügt, in C# 4.0.
+1. @Rachel, deine Erwartung ist einfach unerwartet. Relation zwischen Klassen bedeutet nicht, jede Beziehung zwischen Klassen, die Sie als generische Argumente. I. e.
List<Base>
undList<Derived>
Geschwister sind (haben die gemeinsame Basis-Schnittstelle), nicht aber abgeleitet von einander in keiner Weise. Es gibt viele Beiträge zu diesem Thema - Suche nach "C# Kovarianz" (hinzufügen können ", so Eric Lippert" für bessere Ergebnisse).InformationsquelleAutor Servy