Laden...

Objekt einer Oberklasse in ein Objekt einer Unterklasse casten

Erstellt von derape vor 14 Jahren Letzter Beitrag vor 4 Jahren 12.009 Views
D
derape Themenstarter:in
1 Beiträge seit 2009
vor 14 Jahren
Objekt einer Oberklasse in ein Objekt einer Unterklasse casten

Hallo zuammen,

meine Frage ist eher genereller Natur. Ich dachte eigentlich die OOP-Prinzipien verstanden zu haben 😉
Hier meine Situation:


private class BaseClass
{
  public int X;
}

private class ExtendedClass : BaseClass
{
  public int Y;
}

Eine einfache Basisklasse sowie eine abgeleitete Klasse. In meinem Fall ist die Basisklasse viel komplexer und ich moechte sie lediglich erweitern.

Ich habe nun ein Objekt der Basis klasse und moechte den Inhalt quasi auf die erweiterte Basisklasse kopieren.


  BasisClass bc = new BasisClass();
  bc.X = 42; 
  ExtendedClass ec = bc;

C# laesst hier keine implizite Konversion zu. Wenn ich versuche die Basisklasse zu der Extendedklasse zu casten bekomme ich einen null-Wert zurueck. Woran liegt das? Denn eigentlich enthaelt doch die Extendedklasse alle Informationen der Basisklasse...

Folgendes ginge (was ich nicht wirklich verstehe warum)


  BasisClass bc = new ExtendedClass();
  (bc as ExtendedClass).Y = 44;

Jetzt muesste ich aber allerdings alle Eigenschaften der Klasse quasi auf die ExtendedClass kopieren...Gibt es auch einen "schnelleren" Weg wie im 2. Codebeispiel?

Vielen Dank im Vorraus

49.485 Beiträge seit 2005
vor 14 Jahren

Hallo derape,

ein Objekt ist immer vom Typ einer bestimmten Klasse und der Typ des Objekts ist nicht änderbar. Der Typ eines Objekts steht also fest. Bei Casten ändert man normalerweise nur den Typ des Ausdrucks, nicht den Typ des Objekts. Außerdem sind Variablen und Objekte zwei ganz unterschiedliche Paar Schuh. Du musst zwischen statischem Typ und dynamischen Typ einer Variablen unterscheiden.

Nach der Zuweisung

BaseClass  bc = new ExtendedClass ();

ist die Variable, die den statischen Typ BaseClass hat, dann vom dynamischen Typ ExtendedClass, weil darin ein Objekt vom Typ ExtendedClass gespeichert ist. Die Zuweisung ändert aber weder was am Typ der Variablen noch was am Typ des Objekts.

Vererbung ist ein IST-EIN-Beziehung. Nehmen wir das beliebte Säugetier und Hund Beispiel. Ein Hund (Unterklasse) ist ein Säugetier (Oberklasse), aber ein Säugetier ist nicht zwangsläufig ein Hund. Deshalb geht deine Zuweisung nicht (verkehrte Richtung), auch mit einem Cast (der nur den statischen Typ einer eines Ausdrucks ändert) nicht.

Verabschiede dich von der Idee, dass du zur Laufzeit aus einem Objekt der Oberklasse ein Objekt der Unterklasse machen kannst. Das würde man zwar irgendwie hinbekommen (durch ein benutzerdefinierte Typumwandlung), aber das widerspricht den OOP-Prinzipien, nach denen der Typ eines Objekts zum Erzeugungszeitpunkt unwiderruflich festgelegt wird.

Noch ein Wort zu Variablen: Variablen vom Typ einer Klasse sind in C# Referenzvariablen. Das heißt, wenn ich geschrieben habe, eine Variable enthält ein Objekt, dann bedeutet das in Wahrheit, dass die Variable (nur) eine Referenz auf das Objekt enthält. Daran sieht man nochmal, dass Variablen und Objekte zwei ganz unterschiedliche Sachen sind.

herbivore

N
1 Beiträge seit 2010
vor 13 Jahren

Servus,

bin bei Internet Recherche über diesen Thread gestolpert da ich ein ähnliches Problem hatte und wollte einfach mal mein eLösung präsentieren

Mein Problem war, dass ich Objekte von einem WCF Service erhalte, die ich um einige Properties zwecks Anzeige erweitern musste.
Da ich nicht jedes Property einzeln der Subklasse zuweisen wollte habe ich als eleganteste Lösung Reflektion gewählt.


class BaseClass 
{ 
    public int x;
}

class ExtendedClass : BaseClass 
{
    public int y;

    public ExtendedClass(BaseClass baseClass)
    {
        PropertyInfo[] Properties = baseClass.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
 
        for(int i = 0; i < Properties.Length; i++)
        {
            PropertyInfo propertyInfo = Properties[i];
            var value = propertyInfo.GetValue(baseClass, null);
            if(value != null)
            {
                propertyInfo.SetValue(this, value, null);
            }
        }
}

Hoffe das es jemanden weiterhelfen kann.

F
2 Beiträge seit 2019
vor 4 Jahren

Hallo Leute,

ich bin ganz neu hier und habe ein Problem bei einer Belegarbeit für Info (Teil von meinem Maschinenbaustudium). Ich suche schon seit ungefähr einer Stunde nach einer praktikablen Lösung, aber finde leider nichts (weder die interne Suche noch google hat mich bisher weiter gebracht).
Bitte nehm mir nicht übel, dass ich so einen alten Thread ausgrabe, aber ich wollte nich unbedingt einen neuen aufmachen und dachte, dass das hier reinpasst. Es geht um eine abstrakte Oberklasse und deren Unterklassen. Das Problem besteht darin, dass ich mittels try-catch eine Variable vom Typ der abstrakten Oberklasse in eine Variable vom Typ der Unterklasse schreiben will und dafür einen praktikablen Weg suche.

Hier kommen die Details:

Das Framework dieses Projektes stellt mir eine abstrakte Oberklasse.


public abstract class Oberklasse
{
    //Member der Oberklasse
    private string name = null;

    public string GetName();    //Gibt den Namen des Objektes. Der Name wird im Konstruktor festgelegt.
}

Von dieser Oberklasse habe ich mehrere Unterklassen abgeleitet.


public class Unterklasse1 : Oberklasse
{
    //Member der Unterklasse1
}

public class Unterklasse2 : Oberklasse
{
    //Member der Unterklasse2
}

public class Unterklasse3 : Oberklasse
{
    //Member der Unterklasse3
}

An einer anderen Stelle habe ich eine Funktion. Diese Funktion bekommt als Argument eine Liste vom Typ Oberklasse, die also Objekte aller Unterklassen enthalten kann. Aus dieser Liste sollen jetzt alle Objekte vom Typ Unterklasse3 gesucht und als Liste zurückgegeben werden. Ich kann diese Funktion auch nicht grundsätzlich umgehen, da die Liste "oberliste" vom Framework vorgegeben wird und ich den entsprechenden Rückgabewert für das Programm brauche.
Im Moment habe ich das folgendermaßen gelöst:


public List<Unterklasse3> SucheObjekteDerKlasse3(List<Oberklasse> oberliste)
{
    List<Unterklasse3> unterliste = new List<Unterklasse3>;
    try
    {
        foreach (Unterklasse3 unterobjekt3 in oberliste)
            unterliste.Add(unterobjekt3)
    }
    catch (InvalidCastException)
    {
        //In diesem Fall stimmt er Typ nicht überein und es soll nichts passieren.
    }
    return unterliste;
}

Das Problem an dieser Umsetzung ist natürlich, dass das foreach nach der ersten Exception abgebrochen wird. Wenn also als allererstes in der Liste ein Objekt vom Typ Unterobjekt1 steht, ist der Rückgabewert eine leere Liste.

Ich habe auch schon probiert, ob ich das über eine for-Schleife lösen kann:


...
    for (int i = 0; i < oberliste.Count; i++)
    {
        try
        {
            unterliste.Add(oberliste[i]);//wird vom Debugger nicht zugelassen
        }
        catch (InvalidCastException)
        {  }
    }

Aber bei dieser Umsetzung kann ich gar nicht kompilieren, weil der Debugger rummeckert, weil die Typen von oberliste und unterliste nicht übereinstimmen.

Weiß jemand von euch zufällig eine Möglichkeit, wie ich diese Funktion umsetzen kann? Ich habe lange gesucht, wie ich eine Variable ok vom Typ Oberklasse in eine Variable uk3 vom Typ Unterklasse3 schreiben kann. Theoretisch müsste das ja möglich sein, wenn das in ok gespeicherte Objekt vom Typ Unterklasse 3 ist, aber der Debugger lässt das leider auch dann nicht zu, wenn ich die InvalidCastException abfange.

Ich habe auch gesucht wie ich try-catch und foreach sinnvoll kombinieren kann, aber das ist in diesem Fall scheinbar nicht möglich. Wenn ich das try-catch in die foreach-schleife schreibe, passiert während der Laufzeit ein Fehler, was ja auch klar ist.

Falls mir keiner eine Lösung aufzeigen kann, werde ich diese Unterscheidung über den Namen der Objekte durchführen. Theoretisch sind die Objekte nach ihrem Typ benannt, heißen also in etwa "Unterklasse1_03" oder "Unterklasse3_42". Praktisch werden die Namen durch eine Konfigurationsdatei vorgegeben, und wenn sich jemand nicht an diese Namenskonvention hält (z.B. ein fieser Korrektor beim Bewerten), kann das auch wieder zu Problemen führen. Deswegen würde ich einen eleganteren Weg bevorzugen.

1.029 Beiträge seit 2010
vor 4 Jahren

Hi,

hier gibt es verschiedene Wege zum Ziel.

Grundlegend wirst du casten müssen, casten tut man grundlegend so:


var mySubObj = (MySubClass)myObj;

(Auch hierbei tritt eine Exception auf, wenn das Objekt eben keine Instanz von MySubClass ist)

Um nun vorab zu prüfen, ob so ein Cast überhaupt möglich ist - verwendet man den "is"-Operator, also:


if (myObj is MySubClass)
{
var mySubObj = (MySubClass)myObj;
}

Ich denke den Schleifenteil kriegst du dann schon hin.

LG

PS: Für die Zukunft - bitte trotzdem einen neuen Thread pro Frage öffnen - so zwingst du nur einen Mod es manuell abzuteilen - also unnötige Arbeit.

C
224 Beiträge seit 2009
vor 4 Jahren

oder


SomeClass someObject = obj as SomeClass;
if (someObject != null)
{
  //Cast erfolgreich
   ...
}

W
955 Beiträge seit 2010
vor 4 Jahren

oberliste.Where(p => p is Unterklasse3).Cast<Unterklasse3>().ToList();

F
2 Beiträge seit 2019
vor 4 Jahren

Vielen Dank für die extrem schnellen Antworten, das war echt hilfreich. Ich habe es so gemacht, wie es Taipi vorgeschlagen hat, und habe dadurch wieder ein bisschen was gelernt.

Vielen Dank dafür.

Edit: Dass ich den Mods damit Arbeit mache, tut mir ehrlich gesagt leid. Das war mir nicht bewusst.