Frage Kann nicht von der übergeordneten Klasse in die untergeordnete Klasse umwandeln


Ich versuche, von einer Elternklasse in eine Kindklasse zu transformieren, erhalte aber eine InvalidCastException. Die Kindklasse hat nur eine Eigenschaft vom Typ int. Weiß jemand, was ich tun muss?


75
2018-06-12 19:39


Ursprung


Antworten:


Du kannst kein Säugetier in einen Hund werfen - es könnte eine Katze sein.

Du kannst kein Essen in ein Sandwich werfen - es könnte ein Cheeseburger sein.

Sie können kein Auto in einen Ferrari werfen - es könnte eine Honda sein, oder spezifischer Sie können einen Ferrari 360 Modena nicht zu einem Ferrari 360 Challange Stradale gießen - es gibt verschiedene Teile, obwohl beide Ferrari 360s sind.


97
2018-05-12 15:44



Eine einfache Möglichkeit, in C # zu reduzieren, besteht darin, das übergeordnete Element zu serialisieren und es anschließend in das untergeordnete Element zu deserialisieren.

 var serializedParent = JsonConvert.SerializeObject(parentInstance); 
 Child c  = JsonConvert.DeserializeObject<Child>(serializedParent);

Ich habe eine einfache Konsole App, die Tier in Hund wirft, mit den oben genannten zwei Zeilen Code über Hier


87
2017-12-21 23:10



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 der Cast erfolgreich ist, muss die Instanz, die Sie downcasting sind, eine Instanz der Klasse sein, in die Sie downcasting gehen (oder zumindest die Klasse, in die Sie downcasting sind, muss innerhalb der Klassenhierarchie der Instanz sein) Besetzung wird fehlschlagen.


53
2018-06-12 19:40



Es gibt einige Fälle, in denen eine solche Besetzung Sinn machen würde.
In meinem Fall erhielt ich über das Netzwerk eine BASE-Klasse, und ich brauchte mehr Funktionen. Also habe ich es abgeleitet, um es mit all den Schnickschnacks, die ich wollte, auf meiner Seite zu behandeln, und die empfangene BASE-Klasse in die DERIVED-Klasse zu werfen, war einfach keine Option (löst InvalidCastException of Course)

Ein praktisches denke mal kreativ LÖSUNG war, eine EXTENSION Helper-Klasse zu deklarieren, die BASE-Klasse NICHT tatsächlich erbte, sondern IT als Mitglied enthielt.

public class BaseExtension
{
   Base baseInstance;

   public FakeDerived(Base b)
   {
      baseInstance = b;
   }

   //Helper methods and extensions to Base class added here
}

Wenn Sie lose Kopplung haben und nur ein paar zusätzliche Features zur Basisklasse benötigen JA WIRKLICH Da es ein absolutes Bedürfnis nach Ableitung gibt, könnte dies eine schnelle und einfache Problemumgehung sein.


14
2017-10-27 15:30



Das würde objektorientierte Prinzipien verletzen. Ich würde sagen, eine elegante Lösung hier und anderswo im Projekt verwendet ein Objekt-Mapping-Framework wie AutoMapper um eine Projektion zu konfigurieren.

Hier ist eine etwas komplexere Konfiguration als nötig, aber für die meisten Fälle flexibel genug:

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 startet, rufen Sie an AutoMapperConfiguration.Configure() und dann kannst du so projizieren:

ChildClassOne child = Mapper.Map<BaseClass, ChildClassOne>(baseClass);

Eigenschaften werden per 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 optimieren. Siehe die Dokumentation .


13
2018-04-01 23:47



Paul, du hast nicht gefragt "Kann ich es tun" - ich nehme an, du willst es wissen Wie es zu tun!

Wir mussten dies für ein Projekt tun - es gibt viele Klassen, die wir nur einmal generisch einrichten und dann Eigenschaften initialisieren, die für abgeleitete Klassen spezifisch sind. Ich benutze VB so mein Beispiel ist in VB (harte Noogies), aber ich habe das VB-Beispiel von dieser Seite gestohlen, die auch eine bessere C # -Version hat:

http://www.eggheadcafe.com/tutorials/aspnet/a4264125-fcb0-4757-9d78-ff541dfbcb56/net-reflection--copy-cl.aspx

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 wirft das nicht wirklich. Es erstellt ein neues abgeleitetes Objekt und kopiert die Eigenschaften vom übergeordneten Element, 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, nicht Member (öffentliche Variablen) in der Klasse kopiert werden (aber Sie könnten es erweitern, um das zu tun, wenn Sie öffentliche Elemente schamlos machen wollen).

Casting erzeugt im Allgemeinen 2 Variablen, die auf das gleiche Objekt zeigen (Mini-Tutorial hier, bitte keine Eckfall-Ausnahmen bei mir werfen). Dies hat erhebliche Auswirkungen (Übung für den Leser)!

Natürlich muss ich sagen, warum die Sprache dich 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) nehmen und in einer Variablen vom Typ Winforms-Steuerelement speichern können. Natürlich kann das Steuerelement das Objekt um OK bewegen, und Sie können mit allen "Kontroll-y" -Dingen über das Textfeld umgehen (z. B. oben, links, Text-Eigenschaften). Das textboxspezifische Zeug (z. B. .multiline) kann nicht gesehen werden, ohne dass die Variable vom Typ "control" auf das Textfeld im Speicher verweist, aber es ist immer noch im Speicher vorhanden.

Stellen Sie sich vor, Sie haben ein Steuerelement, und Sie möchten eine Variable vom Typ textbox darauf anwenden. Das Steuerelement im Speicher fehlt "Multiline" und andere Textbox-Dinge. Wenn Sie versuchen, sie zu referenzieren, wird das Steuerelement eine Multiline-Eigenschaft nicht magisch vergrößern! Die Eigenschaft (sieh es dir hier wie eine Membervariable an, die tatsächlich einen Wert speichert - weil es im Speicher der Instanz der Textbox ein ist) Muss existieren. Denken Sie daran, dass es sich um dasselbe Objekt handelt, auf das Sie verweisen. Es ist also keine Sprachbeschränkung, es ist philosophisch unmöglich, so zu argumentieren.


9
2018-05-12 15:40



Ich habe die meisten Leute gesehen, die sagten ausdrückliches Elternteil zum Kindcasting ist nicht möglich, das ist eigentlich nicht wahr. Lassen Sie uns einen überarbeiteten Start nehmen und versuchen, es anhand von Beispielen zu beweisen.

Wie wir in .net wissen, haben alle Castings zwei große Kategorien.

  1. Für Werttyp
  2. Für Referenztyp (in Ihrem Fall der Referenztyp)

Der Referenztyp hat drei weitere Situationsfälle, in denen jedes Szenario liegen kann.

Child to Parent (Implizites Casting - Immer erfolgreich)

Fall 1. Kind an jedes direkte oder indirekte Elternteil

Employee e = new Employee();
Person p = (Person)e; //Allowed

Parent to Child (Explizites Casting - Kann erfolgreich sein)

Fall 2. Parent-Variable, die übergeordnete Objekte enthält (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, die untergeordnetes Objekt enthält (Immer erfolgreich)

Hinweis: Da Objekte polymorphe Eigenschaften haben, ist es möglich, dass eine Variable eines übergeordneten Klassentyps einen untergeordneten Typ enthält.

Person p = new Employee(); // p actually is Employee
Employee e = (Employee)p; // Casting allowed

Fazit : Nachdem Sie vor allem gelesen haben, hoffe ich, dass es jetzt Sinn ergibt, wie eine Konvertierung von Eltern zu Kindern möglich ist (Fall 3).

Antwort auf die Frage :

Deine Antwort ist   im 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, sollten Sie immer einen sicheren Pfad wählen.

Um solche Ausnahmesituationen zu vermeiden, hat .net die Verwendung empfohlen ist als Betreiber werden Ihnen helfen, fundierte Entscheidungen zu treffen und ein sicheres Casting zu gewährleisten.


5
2017-10-17 14:56



Die Instanz des Objekts sollte mit dem Typ der untergeordneten Klasse erstellt werden. Sie können eine übergeordnete Typinstanz nicht in einen untergeordneten Typ umwandeln


3
2017-10-22 08:13



Ab C # 7.0 können Sie verwenden Das ist ein Schlüsselwort um dies zu tun :

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 */
    }
}

Was wäre äquivalent zu:

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!


2
2017-08-02 14:17