Ich versuche, von einer übergeordneten Klasse in eine untergeordnete Klasse umzuwandeln, erhalte jedoch eine InvalidCastException. Die untergeordnete Klasse hat nur eine Eigenschaft vom Typ int. Weiß jemand, was ich tun muss?
Ich versuche, von einer übergeordneten Klasse in eine untergeordnete Klasse umzuwandeln, erhalte jedoch eine InvalidCastException. Die untergeordnete Klasse hat nur eine Eigenschaft vom Typ int. Weiß jemand, was ich tun muss?
Antworten:
Eine einfache Möglichkeit zum Downcasting in C # besteht darin, das übergeordnete Element zu serialisieren und es dann in das untergeordnete Element zu deserialisieren.
var serializedParent = JsonConvert.SerializeObject(parentInstance);
Child c = JsonConvert.DeserializeObject<Child>(serializedParent);
Ich habe eine einfache Konsolenanwendung , das Tier in Hund wirft, die oben genannten zwei Zeilen Code mit über hier
Sie können ein Säugetier nicht in einen Hund verwandeln - es könnte eine Katze sein.
Sie können ein Essen nicht in ein Sandwich werfen - es könnte ein Cheeseburger sein.
Sie können kein Auto in einen Ferrari verwandeln - es könnte ein Honda sein, oder genauer gesagt, Sie können keinen Ferrari 360 Modena in einen Ferrari 360 Challange Stradale verwandeln - es gibt verschiedene Teile, obwohl beide Ferrari 360 sind.
Die Instanz, auf die sich Ihre Basisklassenreferenz bezieht, ist keine Instanz Ihrer untergeordneten Klasse. Da ist nichts falsch.
Genauer:
Base derivedInstance = new Derived();
Base baseInstance = new Base();
Derived good = (Derived)derivedInstance; // OK
Derived fail = (Derived)baseInstance; // Throws InvalidCastException
Damit die Umwandlung erfolgreich ist, muss die Instanz, auf die Sie das Downcasting durchführen, eine Instanz der Klasse sein, auf die Sie das Downcasting durchführen (oder zumindest muss die Klasse, auf die Sie das Downcasting durchführen, innerhalb der Klassenhierarchie der Instanz liegen), andernfalls die Besetzung wird scheitern.
Es gibt einige Fälle, in denen eine solche Besetzung sinnvoll wäre.
In meinem Fall erhielt ich eine BASE-Klasse über das Netzwerk, und ich brauchte mehr Funktionen dafür. Es war also einfach keine Option, es abzuleiten, um es auf meiner Seite mit all den Schnickschnack zu handhaben, die ich wollte, und die empfangene BASE-Klasse in die DERIVED-Klasse zu verwandeln (Throws InvalidCastException of Course).
Eine praktische Think-out-of-the-box war SOLUTION eine Erweiterung Helper - Klasse zu deklarieren , die war nicht BASE Klasse erbt tatsächlich, aber einschließlich IT als Mitglied.
public class BaseExtension
{
Base baseInstance;
public FakeDerived(Base b)
{
baseInstance = b;
}
//Helper methods and extensions to Base class added here
}
Wenn Sie eine lose Kopplung haben und nur ein paar zusätzliche Funktionen für die Basisklasse benötigen, ohne WIRKLICH eine absolute Ableitung zu benötigen, kann dies eine schnelle und einfache Problemumgehung sein.
BaseExtensionhier zumindest so implementiert wird IBase, dass Sie es in ähnlichen Kontexten verwenden können? Oder war es nicht wichtig für deine Bedürfnisse?
Ich habe die meisten Leute gesehen, die sagten, explizites Casting von Eltern zu Kindern sei nicht möglich, das ist eigentlich nicht wahr. Nehmen wir einen überarbeiteten Anfang und versuchen Sie ihn anhand von Beispielen zu beweisen.
Wie wir in .net wissen, haben alle Castings zwei große Kategorien.
Der Referenztyp hat drei weitere Hauptsituationen, in denen jedes Szenario liegen kann.
Fall 1. Kind eines direkten oder indirekten Elternteils
Employee e = new Employee();
Person p = (Person)e; //Allowed
Fall 2. Übergeordnete Variable mit übergeordnetem Objekt (nicht zulässig)
Person p = new Person(); // p is true Person object
Employee e = (Employee)p; //Runtime err : InvalidCastException <-------- Yours issue
Fall 3. Übergeordnete Variable mit untergeordnetem Objekt (immer erfolgreich)
Hinweis: Da Objekte polymorpher Natur sind, kann eine Variable eines übergeordneten Klassentyps einen untergeordneten Typ enthalten.
Person p = new Employee(); // p actually is Employee
Employee e = (Employee)p; // Casting allowed
Fazit: Hoffen Sie nach dem Lesen vor allem, dass es jetzt Sinn macht, wie eine Konversion von Eltern zu Kindern möglich ist (Fall 3).
Antwort auf die Frage :
Ihre Antwort ist in Fall 2. Wo Sie sehen können, dass ein solches Casting von OOP nicht erlaubt ist und Sie versuchen, eine der Grundregeln von OOP zu verletzen. Wählen Sie daher immer einen sicheren Pfad.
Um solche Ausnahmesituationen zu vermeiden, hat .net empfohlen, is / as- Operatoren zu verwenden, die Ihnen helfen, fundierte Entscheidungen zu treffen und ein sicheres Casting zu gewährleisten.
Das würde gegen objektorientierte Prinzipien verstoßen. Ich würde sagen, dass eine elegante Lösung hier und anderswo im Projekt die Verwendung eines Objektzuordnungsframeworks wie AutoMapper zum Konfigurieren einer Projektion ist.
Hier ist eine etwas komplexere Konfiguration als erforderlich, die jedoch für die meisten Fälle flexibel genug ist:
public class BaseToChildMappingProfile : Profile
{
public override string ProfileName
{
get { return "BaseToChildMappingProfile"; }
}
protected override void Configure()
{
Mapper.CreateMap<BaseClass, ChildClassOne>();
Mapper.CreateMap<BaseClass, ChildClassTwo>();
}
}
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<BaseToChildMappingProfile>();
});
}
}
Wenn die Anwendung den Aufruf startet AutoMapperConfiguration.Configure()und Sie dann wie folgt projizieren können:
ChildClassOne child = Mapper.Map<BaseClass, ChildClassOne>(baseClass);
Eigenschaften werden gemäß Konvention zugeordnet. Wenn die Klasse geerbt wird, sind die Eigenschaftsnamen genau gleich und die Zuordnung wird automatisch konfiguriert. Sie können zusätzliche Eigenschaften hinzufügen, indem Sie die Konfiguration anpassen. Siehe die Dokumentation .
Paul, du hast nicht gefragt, ob ich es tun kann - ich gehe davon aus, dass du wissen willst, wie es geht!
Wir mussten dies für ein Projekt tun - es gibt viele Klassen, die wir nur einmal generisch eingerichtet haben und dann Eigenschaften initialisieren, die für abgeleitete Klassen spezifisch sind. Ich benutze VB, also ist mein Sample in VB (harte Noogies), aber ich habe das VB-Sample von dieser Seite gestohlen, die auch eine bessere C # -Version hat:
Beispielcode:
Imports System
Imports System.Collections.Generic
Imports System.Reflection
Imports System.Text
Imports System.Diagnostics
Module ClassUtils
Public Sub CopyProperties(ByVal dst As Object, ByVal src As Object)
Dim srcProperties() As PropertyInfo = src.GetType.GetProperties
Dim dstType = dst.GetType
If srcProperties Is Nothing Or dstType.GetProperties Is Nothing Then
Return
End If
For Each srcProperty As PropertyInfo In srcProperties
Dim dstProperty As PropertyInfo = dstType.GetProperty(srcProperty.Name)
If dstProperty IsNot Nothing Then
If dstProperty.PropertyType.IsAssignableFrom(srcProperty.PropertyType) = True Then
dstProperty.SetValue(dst, srcProperty.GetValue(src, Nothing), Nothing)
End If
End If
Next
End Sub
End Module
Module Module1
Class base_class
Dim _bval As Integer
Public Property bval() As Integer
Get
Return _bval
End Get
Set(ByVal value As Integer)
_bval = value
End Set
End Property
End Class
Class derived_class
Inherits base_class
Public _dval As Integer
Public Property dval() As Integer
Get
Return _dval
End Get
Set(ByVal value As Integer)
_dval = value
End Set
End Property
End Class
Sub Main()
' NARROWING CONVERSION TEST
Dim b As New base_class
b.bval = 10
Dim d As derived_class
'd = CType(b, derived_class) ' invalidcast exception
'd = DirectCast(b, derived_class) ' invalidcast exception
'd = TryCast(b, derived_class) ' returns 'nothing' for c
d = New derived_class
CopyProperties(d, b)
d.dval = 20
Console.WriteLine(b.bval)
Console.WriteLine(d.bval)
Console.WriteLine(d.dval)
Console.ReadLine()
End Sub
End Module
Natürlich ist das nicht wirklich Casting. Es wird ein neues abgeleitetes Objekt erstellt und die Eigenschaften vom übergeordneten Objekt kopiert, wobei die untergeordneten Eigenschaften leer bleiben. Das ist alles was ich tun musste und es klingt wie alles was du tun musst. Beachten Sie, dass nur Eigenschaften kopiert werden, keine Mitglieder (öffentliche Variablen) in der Klasse (Sie können dies jedoch erweitern, wenn Sie sich schämen, öffentliche Mitglieder zu entlarven).
Beim Casting werden im Allgemeinen zwei Variablen erstellt, die auf dasselbe Objekt verweisen (Mini-Tutorial hier, bitte werfen Sie keine Ausnahmen für Eckfälle auf mich). Dies hat erhebliche Konsequenzen (Übung für den Leser)!
Natürlich muss ich sagen, warum die Sprache Sie nicht von der Basis zur Ableitung der Instanz gehen lässt, sondern umgekehrt. Stellen Sie sich einen Fall vor, in dem Sie eine Instanz eines Winforms-Textfelds (abgeleitet) in einer Variablen vom Typ Winforms-Steuerelement speichern können. Natürlich kann das 'Steuerelement' das Objekt in Ordnung verschieben und Sie können alle 'Steuerelement-y'-Dinge über das Textfeld erledigen (z. B. oben, links, .text-Eigenschaften). Das textfeldspezifische Material (z. B. .multiline) kann nicht angezeigt werden, ohne dass die Variable vom Typ 'control' umgewandelt wird, die auf das Textfeld im Speicher zeigt, aber es befindet sich immer noch im Speicher.
Stellen Sie sich nun vor, Sie haben ein Steuerelement und möchten eine Variable vom Typ Textfeld einfügen. Dem Steuerelement im Speicher fehlen "mehrzeilige" und andere Textfelder. Wenn Sie versuchen, auf sie zu verweisen, wird das Steuerelement eine mehrzeilige Eigenschaft nicht auf magische Weise vergrößern! Die Eigenschaft (sehen Sie sie hier wie eine Mitgliedsvariable an, die tatsächlich einen Wert speichert - da sie im Speicher der Textboxinstanz aktiviert ist) muss vorhanden sein. Denken Sie beim Casting daran, dass es sich um dasselbe Objekt handeln muss, auf das Sie zeigen. Daher handelt es sich nicht um eine Sprachbeschränkung, es ist philosophisch unmöglich, dies so zu tun.
Für mich war es genug, alle Eigenschaftsfelder von der Basisklasse wie folgt auf das übergeordnete Element zu kopieren:
using System.Reflection;
public static ChildClass Clone(BaseClass b)
{
ChildClass p = new ChildClass(...);
// Getting properties of base class
PropertyInfo[] properties = typeof(BaseClass).GetProperties();
// Copy all properties to parent class
foreach (PropertyInfo pi in properties)
{
if (pi.CanWrite)
pi.SetValue(p, pi.GetValue(b, null), null);
}
return p;
}
Eine universelle Lösung für jedes Objekt finden Sie hier
Ab C # 7.0 können Sie dazu das Schlüsselwort is verwenden:
Mit diesen definierten Klassen:
class Base { /* Define base class */ }
class Derived : Base { /* Define derived class */ }
Sie können dann etwas tun wie:
void Funtion(Base b)
{
if (b is Derived d)
{
/* Do something with d which is now a variable of type Derived */
}
}
Welches wäre gleichbedeutend mit:
void Funtion(Base b)
{
Defined d;
if (b is Derived)
{
d = (Defined)b;
/* Do something with d */
}
}
Sie könnten jetzt anrufen:
Function(new Derived()); // Will execute code defined in if
Ebenso gut wie
Function(new Base()); // Won't execute code defined in if
Auf diese Weise können Sie sicher sein, dass Ihr Downcast gültig ist und keine Ausnahme auslöst!
Zur Besetzung, der tatsächliche muss Gegenstand einer Art gleich oder abgeleitet von den Sie umwandeln möchten ...
oder, um es anders auszudrücken, der Typ, in den Sie es umwandeln möchten, muss mit dem tatsächlichen Typ des Objekts oder einer Basisklasse davon identisch sein.
Wenn Ihr tatsächliches Objekt vom Typ Baseclass ist , können Sie es nicht in einen abgeleiteten Klassentyp umwandeln ...
Eine Variation des Serialisierungsansatzes für Benutzer von ServiceStack:
var child = baseObject.ConvertTo<ChildType>();
oder umso ausführlicher:
var child = baseObject.ToJson().FromJson<ChildType>();
Die Serialisierung von ServiceStack ist zwar sehr schnell, aber dies ist weder eine Lösung für massive Konvertierungen bei Übertragungen mit geringer Latenz noch für hochkomplexe Typen. Das ist wahrscheinlich für jeden offensichtlich, der ServiceStack verwendet, aber ich dachte, ich würde es im Vorgriff auf Kommentare klarstellen.