Ist es möglich, einer abgeleiteten Klassenreferenz ein Basisklassenobjekt mit einer expliziten Typumwandlung in C # zuzuweisen?
Ich habe es versucht und es entsteht ein Laufzeitfehler.
c#
explicit
derived-class
casting
base-class
Maddy.Shik
quelle
quelle
Derived
, aber Sie können eineDerived
Referenz alsBase
Referenz behandeln.Base
und der andere erstellt eine Instanz vonDerived
. Wenn Sie eine virtuelle Methode aufrufen, fürb
die überschrieben wurdeDerived
, wird dasDerived
Verhalten angezeigt , wenn Sie eine Instanz von habenDerived
. Es ist jedoch nicht wirklich angebracht, in einem Stack Overflow-Kommentarthread auf die Details einzugehen - Sie sollten wirklich ein gutes C # -Buch oder Tutorial lesen, da dies ziemlich grundlegende Dinge sind.Nein, das ist nicht möglich, da das Zuweisen zu einer abgeleiteten Klassenreferenz so aussehen würde, als würde man sagen "Basisklasse ist ein voll funktionsfähiger Ersatz für abgeleitete Klassen, sie kann alles, was die abgeleitete Klasse kann", was nicht zutrifft, da abgeleitete Klassen im Allgemeinen angeboten werden mehr Funktionalität als ihre Basisklasse (zumindest ist das die Idee hinter der Vererbung).
Sie können einen Konstruktor in die abgeleitete Klasse schreiben, indem Sie ein Basisklassenobjekt als Parameter verwenden und die Werte kopieren.
Etwas wie das:
In diesem Fall würden Sie das Basisobjekt kopieren und ein voll funktionsfähiges abgeleitetes Klassenobjekt mit Standardwerten für abgeleitete Elemente erhalten. Auf diese Weise können Sie auch das von Jon Skeet aufgezeigte Problem vermeiden:
quelle
Ich hatte dieses Problem und löste es, indem ich eine Methode hinzufügte, die einen Typparameter verwendet und das aktuelle Objekt in diesen Typ konvertiert.
Das bedeutet, dass Sie es in Ihrem Code wie folgt verwenden können:
quelle
Wie viele andere geantwortet haben: Nein.
Ich verwende den folgenden Code in den unglücklichen Fällen, in denen ich einen Basistyp als abgeleiteten Typ verwenden muss. Ja, es ist ein Verstoß gegen das Liskov-Substitutionsprinzip (LSP), und ja, meistens bevorzugen wir die Komposition gegenüber der Vererbung. Requisiten an Markus Knappen Johansson, auf dessen ursprünglicher Antwort dies basiert.
Dieser Code in der Basisklasse:
Erlaubt:
Da es Reflexion verwendet, ist es "teuer". Verwenden Sie entsprechend.
quelle
user-defined conversions to or from a base class are not allowed
Ich sehe die Gründe dafür, bin aber enttäuscht. wie es so viel Spaß gemacht hätte, wenn es dies erlaubt hätte ..if (type.BaseType != null)
Anweisung zu Markus Knappen Johanssons A hinzugefügt haben . Warum ist das so? Dies bedeutet, dass die Aufrufe eingegeben werden können, die nicht von MyBaseClass abgeleitet sind (oder was auch immer). Mir ist klar, dass es immer noch einen Compilerfehler verursacht, wenn es myDerivedObject zugewiesen wird. Wenn es jedoch nur als Ausdruck verwendet wird, wird es kompiliert und zur Laufzeit einfach ein myDerivedObject erstellt, ohne dass Daten von "myBaseObject" kopiert werden. Ich kann mir keinen Anwendungsfall dafür vorstellen.Lösung mit JsonConvert (anstelle von typecast)
Heute stand ich vor dem gleichen Problem und fand eine einfache und schnelle Lösung für das Problem mit
JsonConvert
.quelle
Nein, es ist nicht möglich, daher Ihr Laufzeitfehler.
Sie können jedoch einer Variablen vom Basisklassentyp eine Instanz einer abgeleiteten Klasse zuweisen.
quelle
Wie alle hier sagten, ist das nicht direkt möglich.
Die Methode, die ich bevorzuge und die ziemlich sauber ist, ist die Verwendung eines Object Mapper wie AutoMapper .
Es übernimmt die Aufgabe, Eigenschaften automatisch von einer Instanz in eine andere (nicht unbedingt vom gleichen Typ) zu kopieren.
quelle
Erweitern der Antwort von @ ybo - dies ist nicht möglich, da die Instanz der Basisklasse keine Instanz der abgeleiteten Klasse ist. Es kennt nur die Mitglieder der Basisklasse und weiß nichts über die Mitglieder der abgeleiteten Klasse.
Der Grund, warum Sie eine Instanz der abgeleiteten Klasse in eine Instanz der Basisklasse umwandeln können, liegt darin, dass die abgeleitete Klasse tatsächlich bereits eine Instanz der Basisklasse ist, da diese Mitglieder bereits vorhanden sind. Das Gegenteil kann nicht gesagt werden.
quelle
Sie können eine Variable , die als Basisklasse eingegeben wird, in den Typ einer abgeleiteten Klasse umwandeln. Dies führt jedoch notwendigerweise eine Laufzeitprüfung durch, um festzustellen, ob das tatsächlich betroffene Objekt vom richtigen Typ ist.
Einmal erstellt, kann der Typ eines Objekts nicht mehr geändert werden (nicht zuletzt hat er möglicherweise nicht dieselbe Größe). Sie können jedoch eine Instanz konvertieren und eine neue Instanz des zweiten Typs erstellen. Sie müssen den Konvertierungscode jedoch manuell schreiben.
quelle
Nein, das ist nicht möglich.
Stellen Sie sich ein Szenario vor, in dem ein ACBus eine abgeleitete Klasse der Basisklasse Bus ist. ACBus verfügt über Funktionen wie TurnOnAC und TurnOffAC, die in einem Feld namens ACState ausgeführt werden. TurnOnAC setzt ACState ein und TurnOffAC setzt ACState aus. Wenn Sie versuchen, TurnOnAC- und TurnOffAC-Funktionen auf Bus zu verwenden, macht dies keinen Sinn.
quelle
}}
Wenn wir ein untergeordnetes Klassenobjekt erstellen, wird das Basisklassenobjekt automatisch initiiert, sodass die Referenzvariable der Basisklasse auf das untergeordnete Klassenobjekt verweisen kann.
aber nicht umgekehrt, da eine untergeordnete Klassenreferenzvariable nicht auf das Basisklassenobjekt verweisen kann, da kein untergeordnetes Klassenobjekt erstellt wird.
Beachten Sie auch, dass die Referenzvariable der Basisklasse nur Basisklassenmitglieder aufrufen kann.
quelle
Es gibt tatsächlich einen Weg, dies zu tun. Überlegen Sie, wie Sie Newtonsoft JSON verwenden können, um ein Objekt von json zu deserialisieren. Fehlende Elemente werden (oder können es zumindest) ignoriert und alle ihm bekannten Elemente ausgefüllt.
Also hier ist, wie ich es gemacht habe. Ein kleines Codebeispiel folgt meiner Erklärung.
Erstellen Sie eine Instanz Ihres Objekts aus der Basisklasse und füllen Sie sie entsprechend aus.
Serialisieren Sie dieses Objekt mithilfe der "jsonconvert" -Klasse von Newtonsoft json in eine json-Zeichenfolge.
Erstellen Sie nun Ihr Unterklassenobjekt, indem Sie es mit der in Schritt 2 erstellten JSON-Zeichenfolge deserialisieren. Dadurch wird eine Instanz Ihrer Unterklasse mit allen Eigenschaften der Basisklasse erstellt.
Das funktioniert wie ein Zauber! Also .. wann ist das nützlich? Einige Leute fragten, wann dies sinnvoll wäre, und schlugen vor, das OP-Schema zu ändern, um der Tatsache Rechnung zu tragen, dass dies mit Klassenvererbung (in .Net) nicht nativ möglich ist.
In meinem Fall habe ich eine Einstellungsklasse, die alle "Basis" -Einstellungen für einen Dienst enthält. Bestimmte Dienste verfügen über mehr Optionen und diese stammen aus einer anderen DB-Tabelle, sodass diese Klassen die Basisklasse erben. Sie haben alle unterschiedliche Optionen. Wenn Sie also die Daten für einen Dienst abrufen, ist es viel einfacher, die Werte ZUERST mit einer Instanz des Basisobjekts zu füllen. Eine Methode, um dies mit einer einzelnen DB-Abfrage zu tun. Gleich danach erstelle ich das Unterklassenobjekt mit der oben beschriebenen Methode. Ich mache dann eine zweite Abfrage und fülle alle dynamischen Werte auf dem Unterklassenobjekt.
Die endgültige Ausgabe ist eine abgeleitete Klasse mit allen festgelegten Optionen. Wenn Sie dies für weitere neue Unterklassen wiederholen, sind nur wenige Codezeilen erforderlich. Es ist einfach und verwendet ein sehr bewährtes Paket (Newtonsoft), damit die Magie funktioniert.
Dieser Beispielcode lautet vb.Net, Sie können ihn jedoch problemlos in c # konvertieren.
quelle
var destObject = JsonConvert.DeserializeObject<DestinationType>(JsonConvert.SerializeObject(srcObject));
. Ich würde dies nur für Unit-Tests und andere nicht produktive "Hacking" verwenden!Könnte nicht relevant sein, aber ich konnte aufgrund seiner Basis Code für ein abgeleitetes Objekt ausführen. Es ist definitiv hackiger als ich es gerne hätte, aber es funktioniert:
...
quelle
Sie können dies mit generischen tun.
Mit diesem Ansatz erhalten Sie drei Vorteile.
quelle
Sie können eine Erweiterung verwenden:
In Code:
quelle
Ich weiß, dass dies alt ist, aber ich habe es eine ganze Weile erfolgreich verwendet.
quelle
Ich habe einige Teile der vorherigen Antworten (dank dieser Autoren) kombiniert und eine einfache statische Klasse mit zwei Methoden zusammengestellt, die wir verwenden.
Ja, es ist einfach, nein, es deckt nicht alle Szenarien ab, ja, es könnte erweitert und verbessert werden, nein, es ist nicht perfekt, ja, es könnte möglicherweise effizienter gemacht werden, nein, es ist nicht das Beste seit geschnittenem Brot, ja, es gibt Vollständige robuste Nuget-Paket-Objekt-Mapper da draußen, die viel besser für den intensiven Gebrauch geeignet sind, usw. usw., yada yada - aber es funktioniert für unsere Grundbedürfnisse :)
Und natürlich wird versucht, Werte von jedem Objekt auf jedes abgeleitete oder nicht abgeleitete Objekt abzubilden (nur die öffentlichen Eigenschaften, die natürlich den gleichen Namen haben - ignorieren den Rest).
VERWENDUNG:
STATIC UTILITY CLASS:
quelle
Sie können einen Kopierkonstruktor verwenden, der den Instanzkonstruktor sofort aufruft, oder wenn Ihr Instanzkonstruktor mehr als Zuweisungen ausführt, muss der Kopierkonstruktor der Instanz die eingehenden Werte zuweisen.
In diesem Beispiel wurde auf die Microsoft C # -Dokumentation unter Konstruktor verwiesen, da dieses Problem in der Vergangenheit aufgetreten war.
quelle
Eine andere Lösung besteht darin, eine Erweiterungsmethode wie folgt hinzuzufügen:
Dann haben Sie einen Konstruktor in jeder abgeleiteten Klasse, der die Basisklasse akzeptiert:
Optional werden auch Zieleigenschaften überschrieben, wenn diese bereits festgelegt sind (nicht null) oder nicht.
quelle
Es sind nicht nur explizite, sondern auch implizite Konvertierungen möglich.
Die C # -Sprache erlaubt solche Konvertierungsoperatoren nicht, aber Sie können sie trotzdem mit reinem C # schreiben und sie funktionieren. Beachten Sie, dass die Klasse, die den impliziten Konvertierungsoperator (
Derived
) definiert, und die Klasse, die den Operator (Program
) verwendet, in separaten Assemblys definiert werden müssen (z. B. befindet sich dieDerived
Klasse in einer, auflibrary.dll
die durch dasprogram.exe
Enthalten derProgram
Klasse verwiesen wird ).Wenn Sie mithilfe der Projektreferenz in Visual Studio auf die Bibliothek verweisen, zeigt VS bei Verwendung der impliziten Konvertierung Kringel an, die jedoch problemlos kompiliert werden können. Wenn Sie nur auf die verweisen
library.dll
, gibt es keine Kringel.quelle
System.Runtime.CompilerServices.SpecialName
Attribute? Die Dokumente für jede Version von der frühesten verfügbaren (2.0) bis zur "aktuellen Version" (4.6? "Jemand? Jemand?") Sagen nicht, was sie tun, sondern "Die SpecialNameAttribute-Klasse wird derzeit in .NET nicht verwendet Framework, ist aber für die zukünftige Verwendung reserviert. " Siehe: [Link] ( msdn.microsoft.com/en-us/library/ms146064(v=vs.100).aspx ).where T : Delegate
oder parametrisierte Eigenschaften, auch bekannt als Indexer usw. usw. usw.).what does System.Runtime.CompilerServices.SpecialName Attribute do?
- Es wird verwendet, um die Methoden zu markieren, die von einigen speziellen Convenience-Konstrukten der übergeordneten .Net-Sprachen erzeugt werden: Eigenschaftszugriffsberechtigte, Ereigniszugriffsberechtigte, Konstruktoren, Operatoren, Indexer usw. Wenn die IL-Methode nicht mit gekennzeichnet ist, wirdspecialname
sie nicht angezeigt als Eigenschaft / Ereignis / Konstruktor und es würde nur als normale Methode erkannt werden. Das manuelle Markieren von Methoden mit entsprechendem Namen mit diesem Attribut erledigt nur manuell einen Teil der Arbeit des Compilers.op_Exponent
Methode und markieren Sie sie mit demspecialname
Attribut.Wie wäre es mit:
quelle
Der beste Weg, um alle Basiseigenschaften zum abgeleiteten Element hinzuzufügen, ist die Verwendung der Reflektion im Costructor. Probieren Sie diesen Code aus, ohne Methoden oder Instanzen zu erstellen.
quelle
Ich bin nicht der Meinung, dass dies nicht möglich ist. Sie können es so machen:
Verwendung:
quelle
var auto =
ist immer noch vom Typsedan
So habe ich das für Felder gelöst. Sie können dieselbe Iteration über Eigenschaften durchführen, wenn Sie möchten. Möglicherweise möchten Sie einige Überprüfungen für
null
usw. durchführen, aber dies ist die Idee.quelle
Sie können das Basisobjekt einfach in JSON serialisieren und dann in das abgeleitete Objekt deserialisieren.
quelle
Nein, siehe diese Frage, die ich gestellt habe - Upcasting in .NET mit Generika
Der beste Weg ist, einen Standardkonstruktor für die Klasse zu erstellen, eine
Initialise
Methode zu konstruieren und dann aufzurufenquelle
Nicht im traditionellen Sinne ... Konvertieren Sie zu Json, dann zu Ihrem Objekt und boomen Sie, fertig! Jesse oben hatte die Antwort zuerst gepostet, aber nicht diese Erweiterungsmethoden verwendet, die den Prozess so viel einfacher machen. Erstellen Sie einige Erweiterungsmethoden:
Legen Sie sie für immer in Ihre Toolbox, dann können Sie dies immer tun:
Ah, die Macht von JSON.
Es gibt ein paar Fallstricke bei diesem Ansatz: 1. Wir erstellen wirklich ein neues Objekt, kein Casting, was wichtig sein kann oder nicht. Private Felder werden nicht übertragen, Konstruktoren mit Parametern werden nicht aufgerufen usw. Wenn unsere Klasse jedoch nicht auf privaten Feldern und Konstruktoren basiert, ist dies eine sehr effektive Methode, um Daten von Klasse zu Klasse zu verschieben, ohne sie zuzuordnen und aufzurufen Konstruktoren, was der Hauptgrund ist, warum wir überhaupt gießen wollen.
quelle