Ist es möglich, eine Basisklasse Objekt einer abgeleiteten Klasse Referenz mit einer expliziten Typumwandlung zuweisen?

StackOverflow https://stackoverflow.com/questions/729527

Frage

Ist es möglich, eine Basisklasse Objekt einer abgeleiteten Klasse Referenz mit einer expliziten Typumwandlung in C # ?.

zuweisen

Ich habe es versucht, und es erzeugt einen Laufzeitfehler.

War es hilfreich?

Lösung

Nein. Ein Verweis auf eine abgeleitete Klasse muß tatsächlich bezieht sich auf eine Instanz der abgeleiteten Klasse (oder null). Ansonsten, wie würden Sie es erwarten verhalten?

Zum Beispiel:

object o = new object();
string s = (string) o;
int i = s.Length; // What can this sensibly do?

Wenn Sie in der Lage sein, eine Instanz des Basistypen auf den abgeleiteten Typen zu konvertieren, empfehle ich Ihnen eine Methode schreiben, eine geeignete abgeleiteten Typ-Instanz zu erstellen. Oder schauen Sie sich Ihren Vererbungsbaum wieder und versuchen so neu zu gestalten, dass Sie müssen dies nicht tun an erster Stelle.

Andere Tipps

Nein, das ist nicht möglich, da es zu einer abgeleiteten Klasse Referenz zuweisen wie wäre zu sagen „Basisklasse ein vollständig in der Lage Ersatz für abgeleitete Klasse ist, kann er alles tun, die abgeleitete Klasse tun kann“, die seit abgeleiteten Klassen nicht wahr ist in der Regel bietet mehr Funktionen als ihre Basisklasse (zumindest, dass die Idee hinter Erbe ist).

Sie können einen Konstruktor in der abgeleiteten Klasse schreiben eine Basisklasse Objekt als Parameter verwendet wird, um die Werte zu kopieren.

So etwas wie folgt aus:

public class Base {
    public int Data;

    public void DoStuff() {
        // Do stuff with data
    }
}

public class Derived : Base {
    public int OtherData;

    public Derived(Base b) {
        this.Data = b.Data;
        OtherData = 0; // default value
    }

    public void DoOtherStuff() {
        // Do some other stuff
    }
}

In diesem Fall würden Sie das Basisobjekt kopieren und ein voll funktionsfähiges abgeleitete Klasse-Objekt mit Standardwerten für abgeleitete Mitglieder erhalten. Auf diese Weise können auch das Problem hingewiesen, von Jon Skeet vermeiden kann:

Base b = new Base();
Dervided d = new Derived();

b.DoStuff();    // OK
d.DoStuff();    // Also OK
b.DoOtherStuff();    // Won't work!
d.DoOtherStuff();    // OK

d = new Derived(b);  // Copy construct a Derived with values of b
d.DoOtherStuff();    // Now works!

Ich hatte dieses Problem und es gelöst, indem ein Verfahren hinzufügen, die einen Typparameter nimmt und wandelt das aktuelle Objekt in dieser Art.

public TA As<TA>() where TA : Base
{
    var type = typeof (TA);
    var instance = Activator.CreateInstance(type);

     PropertyInfo[] properties = type.GetProperties();
     foreach (var property in properties)
     {
         property.SetValue(instance, property.GetValue(this, null), null);
     }

     return (TA)instance;
}

Das bedeutet, dass Sie es in Sie Code verwenden können:

var base = new Base();
base.Data = 1;
var derived = base.As<Derived>();
Console.Write(derived.Data); // Would output 1

Wie viele andere haben geantwortet, Nr.

Ich verwende den folgenden Code auf diesen unglücklichen Gelegenheiten, bei denen ich einen Basistyp als abgeleitete Typ verwenden muß. Ja, es ist ein Verstoß gegen das Liskov Substitutionsprinzip (LSP) und ja die meiste Zeit wir Komposition der Vererbung begünstigen. Requisiten Markus Knappen Johansson, deren ursprüngliche Antwort dies basiert auf.

Dieser Code in der Basisklasse:

    public T As<T>()
    {
        var type = typeof(T);
        var instance = Activator.CreateInstance(type);

        if (type.BaseType != null)
        {
            var properties = type.BaseType.GetProperties();
            foreach (var property in properties)
                if (property.CanWrite)
                    property.SetValue(instance, property.GetValue(this, null), null);
        }

        return (T) instance;
    }

Erlaubt:

    derivedObject = baseObect.As<derivedType>()

Da es Reflexion verwendet, ist es „teuer“. Verwenden Sie entsprechend.

Nein, es ist nicht möglich, daher Ihre Laufzeitfehler.

Sie können aber eine Instanz einer abgeleiteten Klasse auf eine Variable Basisklasse zuordnen.

Wie jeder hier gesagt, das ist direkt nicht möglich.

Die Methode, die ich bevorzugen und ist ziemlich sauber, ist ein Object Mapper wie AutoMapper zu verwenden.

Es wird die Aufgabe des Kopierens Eigenschaften von einer Instanz zur anderen (nicht notwendigerweise die gleiche Art) automatisch tun.

können Sie werfen einen Variable , dass als Basis-Klasse auf den Typ einer abgeleiteten Klasse geschrieben wird; jedoch durch die Notwendigkeit dies wird eine Laufzeitprüfung tun, um zu sehen, ob das eigentliche Objekt des richtigen Typs beteiligt ist.

Nach der Erstellung der type ein Objekt kann nicht geändert werden (nicht zuletzt, ist es vielleicht nicht gleich groß sein). Sie können jedoch konvertieren eine Instanz, eine neue Instanz des zweiten Typs zu schaffen -. Aber Sie müssen manuell den Conversion-Code schreiben

Als Erweiterung @ YBO Antwort - es ist nicht möglich, weil die Instanz, die Sie von der Basisklasse haben nicht wirklich eine Instanz der abgeleiteten Klasse ist. Es kennt nur die Mitglieder der Basisklasse, und wissen nichts über jene der abgeleiteten Klasse.

Der Grund, dass Sie eine Instanz der abgeleiteten Klasse auf eine Instanz der Basisklasse werfen können, ist, weil die abgeleitete Klasse eigentlich schon eine Instanz der Basisklasse ist, da es bereits die Mitglieder hat. Das Gegenteil kann nicht gesagt werden.

Nein, es ist nicht möglich.

Betrachten wir ein Szenario, in dem ein ACBUS ist eine abgeleitete Klasse der Basisklasse Bus. ACBUS verfügt über Funktionen wie TurnOnAC und TurnOffAC die Namen ACState auf einem Feld arbeiten. TurnOnAC setzt ACState zu auf und TurnOffAC setzt ACState ausgeschaltet. Wenn Sie versuchen, TurnOnAC und TurnOffAC Funktionen auf Bus zu benutzen, macht es keinen Sinn.

class Program
{
    static void Main(string[] args)
    {
        a a1 = new b();  
        a1.print();  
    }
}
class a
{
    public a()
    {
        Console.WriteLine("base class object initiated");
    }
    public void print()
    {
        Console.WriteLine("base");
    }
}
class b:a
{
    public b()
    {
        Console.WriteLine("child class object");
    }
    public void print1()
    {
        Console.WriteLine("derived");
    }
}

}

, wenn wir ein Kind Klassenobjekt erstellen, die Basisklasse Objekt wird automatisch so Basisklassenreferenzvariable eingeleitet Kind Klassenobjekt verweisen kann.

, aber nicht umgekehrt, weil ein Kind Klassenreferenzvariable kann nicht auf Basisklasse Objekt zeigen, weil kein Kind Klassenobjekt erstellt wird.

und auch feststellen, dass Basisklasse Referenzgröße nur Basisklasse Mitglied aufrufen können.

Es ist eigentlich eine Möglichkeit, dies zu tun. Überlegen Sie, wie Sie Newtonsoft JSON deserialisieren ein Objekt aus json verwenden könnten. Es wird (oder zumindest) ignorieren Elemente fehlen und alle Elemente zu füllen, dass es etwa nicht kennt.

Also hier ist, wie ich es tat. Ein kleines Codebeispiel wird meine Erklärung folgen.

  1. Erstellen Sie eine Instanz des Objekts von der Basisklasse und füllen Sie es entsprechend an.

  2. Mit der "jsonconvert" Klasse von Newtonsoft json, serialisiert das Objekt in einen JSON-String.

  3. Ihre Unterklasse erstellen Objekt, indem sie mit dem JSON-String deserialisieren in Schritt 2 erstellten Dies wird eine Instanz der Unterklasse mit allen Eigenschaften der Basisklasse erstellen.

Das funktioniert wie ein Zauber! Also .. wenn ist das nützlich? Einige Leute fragten, wenn dieser Sinn machen würde, und schlug vor, die OP Schema ändert die Tatsache anzupassen, dass Sie nicht nativ diese mit Klassenvererbung tun können (in .NET).

In meinem Fall, ich habe eine Klasse, die Einstellungen für einen Dienst all „Basis“ Einstellungen enthält. Spezielle Dienstleistungen haben mehr Optionen und die aus einer anderen DB-Tabelle kommen, so dass diese Klassen erben die Basisklasse. Sie alle haben eine andere Reihe von Optionen. Also, wenn die Daten für einen Dienst abzurufen, ist es viel einfacher, zuerst die Werte bevölkern eine Instanz des Basisobjekts. Eine Methode, dies zu tun mit einer einzigen DB-Abfrage. Gleich danach schaffe ich die Unterklassenobjekt mit der Methode oben beschrieben. Ich habe dann eine zweite Abfrage machen und alle dynamischen Werte in der Unterklasse-Objekt füllen.

Die letzte Ausgabe ist eine abgeleitete Klasse mit allen Optionen eingestellt. Die Wiederholung dieser für zusätzliche neue Unterklassen dauert nur ein paar Zeilen Code. Es ist einfach, und es wird ein sehr bewährtes Paket (Newtonsoft), um die magische Arbeit zu machen.

In diesem Beispiel-Code ist vb.Net, aber man kann zu c # leicht umwandeln.

' First, create the base settings object.
    Dim basePMSettngs As gtmaPayMethodSettings = gtmaPayments.getBasePayMethodSetting(payTypeId, account_id)
    Dim basePMSettingsJson As String = JsonConvert.SerializeObject(basePMSettngs, Formatting.Indented)

    ' Create a pmSettings object of this specific type of payment and inherit from the base class object
    Dim pmSettings As gtmaPayMethodAimACHSettings = JsonConvert.DeserializeObject(Of gtmaPayMethodAimACHSettings)(basePMSettingsJson)

Könnte nicht relevent werden, aber ich war in der Lage Code auf ein abgeleitetes Objekt aufgrund seiner Basis zu laufen. Es ist auf jeden Fall mehr als Hacky ich möchte, aber es funktioniert:

public static T Cast<T>(object obj)
{
    return (T)obj;
}

...

//Invoke parent object's json function
MethodInfo castMethod = this.GetType().GetMethod("Cast").MakeGenericMethod(baseObj.GetType());
object castedObject = castMethod.Invoke(null, new object[] { baseObj });
MethodInfo jsonMethod = baseObj.GetType ().GetMethod ("ToJSON");
return (string)jsonMethod.Invoke (castedObject,null);

Sie können eine Erweiterung verwenden:

public static void CopyOnlyEqualProperties<T>(this T objDest, object objSource) where T : class
    {
        foreach (PropertyInfo propInfo in typeof(T).GetProperties())
            if (objSource.GetType().GetProperties().Any(z => z.Name == propInfo.Name && z.GetType() == propInfo.GetType()))
                propInfo.SetValue(objDest, objSource.GetType().GetProperties().First(z => z.Name == propInfo.Name && z.GetType() == propInfo.GetType()).GetValue(objSource));
    }

Code:

public class BaseClass
{
  public string test{ get; set;}
}
public Derived : BaseClass
{
//Some properies
}

public void CopyProps()
{
   BaseClass baseCl =new BaseClass();
   baseCl.test="Hello";
   Derived drv=new Derived();
   drv.CopyOnlyEqualProperties(baseCl);
   //Should return Hello to the console now in derived class.
   Console.WriteLine(drv.test);

}

Ich weiß, das ist alt, aber ich habe dies erfolgreich für eine ganze Weile verwendet.

   private void PopulateDerivedFromBase<TB,TD>(TB baseclass,TD derivedclass)
    {
        //get our baseclass properties
        var bprops = baseclass.GetType().GetProperties();
        foreach (var bprop in bprops)
        {
            //get the corresponding property in the derived class
            var dprop = derivedclass.GetType().GetProperty(bprop.Name);
            //if the derived property exists and it's writable, set the value
            if (dprop != null && dprop.CanWrite)
                dprop.SetValue(derivedclass,bprop.GetValue(baseclass, null),null);
        }
    } 

Lösung mit JsonConvert (anstelle von Typumwandlung)

Heute stand ich das gleiche Problem und ich fand eine einfache und schnelle Lösung für das Problem mit JsonConvert.

var base = new BaseClass();
var json = JsonConvert.SerializeObject(base);
DerivedClass derived = JsonConvert.DeserializeObject<DerivedClass>(json);

Eine andere Lösung ist Erweiterung Methode hinzufügen wie folgt:

 public static void CopyProperties(this object destinationObject, object sourceObject, bool overwriteAll = true)
        {
            try
            {
                if (sourceObject != null)
                {
                    PropertyInfo[] sourceProps = sourceObject.GetType().GetProperties();
                    List<string> sourcePropNames = sourceProps.Select(p => p.Name).ToList();
                    foreach (PropertyInfo pi in destinationObject.GetType().GetProperties())
                    {
                        if (sourcePropNames.Contains(pi.Name))
                        {
                            PropertyInfo sourceProp = sourceProps.First(srcProp => srcProp.Name == pi.Name);
                            if (sourceProp.PropertyType == pi.PropertyType)
                                if (overwriteAll || pi.GetValue(destinationObject, null) == null)
                                {
                                    pi.SetValue(destinationObject, sourceProp.GetValue(sourceObject, null), null);
                                }
                        }
                    }
                }
            }
            catch (ApplicationException ex)
            {
                throw;
            }
        }

haben dann einen Konstruktor in jede abgeleitete Klasse, die Basisklasse übernimmt:

  public class DerivedClass: BaseClass
    { 
        public DerivedClass(BaseClass baseModel)
        {
            this.CopyProperties(baseModel);
        }
    }

Es wird auch optional Zieleigenschaften überschreiben, wenn bereits festgelegt (nicht null) oder nicht.

  

Ist es möglich, eine Basisklasse Objekt einer abgeleiteten Klasse Referenz mit einer expliziten Typumwandlung in C # ?.

zuweisen

Nicht nur explizit, sondern auch implizite Konvertierungen sind möglich.

Sprache C # ermöglicht nicht eine solche Umwandlung Betreiber, aber man kann sie noch schreiben reine C # und sie arbeiten. Beachten Sie, dass die Klasse, die den impliziten Umwandlungsoperator (Derived) und die Klasse definiert, die den Betreiber verwendet (Program) muß in getrennten Baugruppen definiert werden (zB die Derived Klasse ist in einer library.dll der durch program.exe verwiesen wird, um die Program Klasse enthält).

//In library.dll:
public class Base { }

public class Derived {
    [System.Runtime.CompilerServices.SpecialName]
    public static Derived op_Implicit(Base a) {
        return new Derived(a); //Write some Base -> Derived conversion code here
    }

    [System.Runtime.CompilerServices.SpecialName]
    public static Derived op_Explicit(Base a) {
        return new Derived(a); //Write some Base -> Derived conversion code here
    }
}

//In program.exe:
class Program {
    static void Main(string[] args) {
        Derived z = new Base(); //Visual Studio can show squiggles here, but it compiles just fine.
    }
}

Wenn Sie die Bibliothek mit dem Projektverweis in Visual Studio Referenz, VS zeigt Kringel, wenn Sie die implizite Konvertierung verwenden, aber es kompiliert gut. Wenn Sie nur die library.dll verweisen, gibt es keine Schnörkel.

Sie können dies tun, mit generisch.

public class BaseClass
{
    public int A { get; set; }
    public int B { get; set; }
    private T ConvertTo<T>() where T : BaseClass, new()
    {
         return new T
         {
             A = A,
             B = B
         }
    }

    public DerivedClass1 ConvertToDerivedClass1()
    {
         return ConvertTo<DerivedClass1>();
    }

    public DerivedClass2 ConvertToDerivedClass2()
    {
         return ConvertTo<DerivedClass2>();
    }
}

public class DerivedClass1 : BaseClass
{
    public int C { get; set; }
}

public class DerivedClass2 : BaseClass
{
    public int D { get; set; }
}

Sie erhalten drei Vorteile dieses Ansatzes.

  1. Sie den Code nicht duplizieren
  2. Sie verwenden Reflexion nicht (was langsam ist)
  3. Alle Ihre Umwandlungen sind an einem Ort

I kombiniert einige Teile der früheren Antworten (dank diesen Autoren) und zusammen eine einfache statische Klasse mit zwei Methoden, die wir verwenden.

Ja, es ist einfach, nein, es alle Szenarien gehören nicht, ja es könnte erweitert werden und besser gemacht, nein, es ist nicht perfekt, ja es möglicherweise effizienter gemacht werden könnte, nein, es ist nicht die größte Sache seit geschnittenem Brot, ja, es gibt Voll auf robuste nuget Paket Objekt gibt Mapper heraus auf diese Weise besser für den harten Einsatz sind, etc etc, Blabla - aber es funktioniert für unsere Grundbedürfnisse aber :)

Und natürlich wird es versuchen, Werte für jedes Objekt von einem beliebigen Objekt auf der Karte, abgeleitet ist oder nicht (nur die öffentlichen Eigenschaften, die die gleichen natürlich genannt werden - ignoriert den Rest)

.

Verwendung:

SesameStreetCharacter puppet = new SesameStreetCharacter() { Name = "Elmo", Age = 5 };

// creates new object of type "RealPerson" and assigns any matching property 
// values from the puppet object 
// (this method requires that "RealPerson" have a parameterless constructor )
RealPerson person = ObjectMapper.MapToNewObject<RealPerson>(puppet);

// OR

// create the person object on our own 
// (so RealPerson can have any constructor type that it wants)
SesameStreetCharacter puppet = new SesameStreetCharacter() { Name = "Elmo", Age = 5 };
RealPerson person = new RealPerson("tall") {Name = "Steve"};

// maps and overwrites any matching property values from 
// the puppet object to the person object so now our person's age will get set to 5 and
// the name "Steve" will get overwritten with "Elmo" in this example
ObjectMapper.MapToExistingObject(puppet, person);

STATIC Utility-Klasse:

public static class ObjectMapper
{
    // the target object is created on the fly and the target type 
    // must have a parameterless constructor (either compiler-generated or explicit) 
    public static Ttarget MapToNewObject<Ttarget>(object sourceobject) where Ttarget : new()
    {
        // create an instance of the target class
        Ttarget targetobject = (Ttarget)Activator.CreateInstance(typeof(Ttarget));

        // map the source properties to the target object
        MapToExistingObject(sourceobject, targetobject);

        return targetobject;
    }

    // the target object is created beforehand and passed in
    public static void MapToExistingObject(object sourceobject, object targetobject)
    {
        // get the list of properties available in source class
        var sourceproperties = sourceobject.GetType().GetProperties().ToList();

        // loop through source object properties
        sourceproperties.ForEach(sourceproperty => {

            var targetProp = targetobject.GetType().GetProperty(sourceproperty.Name);

            // check whether that property is present in target class and is writeable
            if (targetProp != null && targetProp.CanWrite)
            {
                // if present get the value and map it
                var value = sourceobject.GetType().GetProperty(sourceproperty.Name).GetValue(sourceobject, null);
                targetobject.GetType().GetProperty(sourceproperty.Name).SetValue(targetobject, value, null);
            }
        });
    }
}

Wie wäre:

public static T As<T>(this object obj)
    {
        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
    }

Die beste Weg, alle Basiseigenschaften abgeleitetes Element hinzuzufügen ist die Verwendung Reflexion in costructor. Versuchen Sie diesen Code, ohne Methoden oder Instanzen zu schaffen.

    public Derived(Base item) :base()
    {

        Type type = item.GetType();

        System.Reflection.PropertyInfo[] properties = type.GetProperties();
        foreach (var property in properties)
        {
            try
            {
                property.SetValue(this, property.GetValue(item, null), null);
            }
            catch (Exception) { }
        }

    }

Ich bin nicht einverstanden, dass es nicht möglich ist. Sie können es tun, wie folgt:

public class Auto 
{ 
    public string Make {get; set;}
    public string Model {get; set;}
}

public class Sedan : Auto
{ 
    public int NumberOfDoors {get; set;}
}

public static T ConvertAuto<T>(Sedan sedan) where T : class
{
    object auto = sedan;
    return (T)loc;
}

Verbrauch:

var sedan = new Sedan();
sedan.NumberOfDoors = 4;
var auto = ConvertAuto<Auto>(sedan);

Nein, sehen Sie diese Frage, die ich fragte - Upcasting in .NET Generika

Der beste Weg ist es, einen Standard-Konstruktor für die Klasse zu machen, konstruieren und dann eine Initialise Methode aufrufen

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top