Ist es möglich, einer abgeleiteten Klassenreferenz mit einem expliziten Typecast ein Basisklassenobjekt zuzuweisen?


87

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.

Antworten:


98

Nein. Ein Verweis auf eine abgeleitete Klasse muss sich tatsächlich auf eine Instanz der abgeleiteten Klasse (oder null) beziehen. Wie würden Sie sonst erwarten, dass es sich verhält?

Beispielsweise:

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

Wenn Sie eine Instanz des Basistyps in den abgeleiteten Typ konvertieren möchten, empfehlen wir Ihnen, eine Methode zum Erstellen einer geeigneten Instanz des abgeleiteten Typs zu schreiben. Oder sehen Sie sich Ihren Vererbungsbaum noch einmal an und versuchen Sie, ihn neu zu gestalten, damit Sie dies nicht erst tun müssen.


72
@ Mike: Der Code wird gut kompiliert. Es fällt jedoch zur Ausführungszeit um :)
Jon Skeet

1
Was passiert dann genau, wenn wir Base b = new Derived () schreiben? ? Werden Objekte sowohl für die Basisklasse als auch für die abgeleitete Klasse erstellt?
Ashif Nataliya

3
@Akie: Nein, es wird ein einzelnes Objekt vom Typ erstellt Derived, aber Sie können eine DerivedReferenz als BaseReferenz behandeln.
Jon Skeet

Gibt es also einen Unterschied im resultierenden Objekt für diese beiden Aussagen? Basis b = neue Basis () und Basis b = neu abgeleitet ()? Was ist der Vorteil einer Verwendung gegenüber der anderen?
Ashif Nataliya

4
@Akie: Ja, einer erstellt eine Instanz von Baseund der andere erstellt eine Instanz von Derived. Wenn Sie eine virtuelle Methode aufrufen, für bdie überschrieben wurde Derived, wird das DerivedVerhalten angezeigt , wenn Sie eine Instanz von haben Derived. 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.
Jon Skeet

46

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:

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 abgeleitetes Klassenobjekt mit Standardwerten für abgeleitete Elemente erhalten. Auf diese Weise können Sie auch das von Jon Skeet aufgezeigte Problem vermeiden:

Base b = new Base();//base class
Derived d = new Derived();//derived class

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!

23

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.

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 Ihrem Code wie folgt verwenden können:

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

Sie sollten den Typ der aktuellen Klasse (Basisklasse) verwenden, um Eigenschaften abzurufen und festzulegen, da dies die Werte sind, die Sie der abgeleiteten Klasse zuordnen möchten.
Bowofola

1
Wenn Sie Eigenschaften haben, in die im abgeleiteten Typ nicht geschrieben werden kann, sollten Sie wahrscheinlich Folgendes ändern: if (property.CanWrite) property.SetValue (instance, property.GetValue (this, null), null);
user3478586

10

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:

    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.


Ich habe es gerade versucht und festgestellt, dass es durch Überladen des expliziten Operators (und des impliziten Operators) weiter verbessert werden könnte. Aber - der Compiler lässt es nicht zu: 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 ..
Henrik

@MEC: Mir ist aufgefallen, dass Sie den Teil "where T: MyBaseClass" gelöscht und die 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.
Tom

@ Tom, späte Antwort, dachte aber, es könnte immer noch nützlich sein. Die beste Antwort auf Ihre Frage wäre wahrscheinlich zu sagen, dass der Name "As" besser "AsOrDefault" gewesen wäre. Im Wesentlichen können wir dieses Ergebnis mit einem Standard vergleichen, wie wir es bei Verwendung von Linqs SingleOrDefault oder FirstOrDefault tun.
MEC

7

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.

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

Ich habe dies unten noch einmal mit Erweiterungsmethoden beantwortet. Ja, das ist die Antwort.
Patrick Knott

5

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

Sie können jedoch einer Variablen vom Basisklassentyp eine Instanz einer abgeleiteten Klasse zuweisen.


5

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.


3

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.


3

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.


2

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.


2
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 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.


2

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.

  1. Erstellen Sie eine Instanz Ihres Objekts aus der Basisklasse und füllen Sie sie entsprechend aus.

  2. Serialisieren Sie dieses Objekt mithilfe der "jsonconvert" -Klasse von Newtonsoft json in eine json-Zeichenfolge.

  3. 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.

' 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)

mit C # und Newtonsoft.Json : var destObject = JsonConvert.DeserializeObject<DestinationType>(JsonConvert.SerializeObject(srcObject));. Ich würde dies nur für Unit-Tests und andere nicht produktive "Hacking" verwenden!
thinkOfaNumber

1

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:

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);

1

Sie können dies mit generischen tun.

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; }
}

Mit diesem Ansatz erhalten Sie drei Vorteile.

  1. Sie duplizieren den Code nicht
  2. Sie verwenden keine Reflexion (die langsam ist)
  3. Alle Ihre Conversions befinden sich an einem Ort

1

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));
    }

In 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);

}

1

Ich weiß, dass dies alt ist, aber ich habe es eine ganze Weile erfolgreich 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);
        }
    } 

1

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:

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 CLASS:

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);
            }
        });
    }
}

1

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.

class Person
{
    // Copy constructor 
    public Person(Person previousPerson)
    {
        Name = previousPerson.Name;
        Age = previousPerson.Age;
    }

    // Copy constructor calls the instance constructor.
    public Person(Person previousPerson)
        : this(previousPerson.Name, previousPerson.Age)
    {
    }

    // Instance constructor.
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public int Age { get; set; }

    public string Name { get; set; }
}

In diesem Beispiel wurde auf die Microsoft C # -Dokumentation unter Konstruktor verwiesen, da dieses Problem in der Vergangenheit aufgetreten war.


0

Eine andere Lösung besteht darin, eine Erweiterungsmethode wie folgt hinzuzufügen:

 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;
            }
        }

Dann haben Sie einen Konstruktor in jeder abgeleiteten Klasse, der die Basisklasse akzeptiert:

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

Optional werden auch Zieleigenschaften überschrieben, wenn diese bereits festgelegt sind (nicht null) oder nicht.


0

Ist es möglich, einer abgeleiteten Klassenreferenz ein Basisklassenobjekt mit einer expliziten Typumwandlung in C # zuzuweisen?

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 die DerivedKlasse in einer, auf library.dlldie durch das program.exeEnthalten der ProgramKlasse verwiesen wird ).

//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 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.


Was für eine schwarze Magie ist das?!? Wie hilft mir "Derived z = new Base ()" bei "BaseCls baseObj; DerivedCls derivativeObj; derivativeObj = (DerivedCls) baseObj" (Q des OP)? Was macht System.Runtime.CompilerServices.SpecialNameAttribute? 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 ).
Tom

> "Was für schwarze Magie ist das?!?" Das nennt man .Net Framework (CLR, IL, BCL). Die Funktionen der IL-, C # - und VB-Sprachen sind nicht identisch. Es gibt Funktionen in VB, die C # nicht unterstützt. Es gibt Funktionen in IL, die C # nicht unterstützt. In C # gibt es Einschränkungen, die eher willkürlich sind und in der zugrunde liegenden IL nicht vorhanden sind (wie where T : Delegateoder parametrisierte Eigenschaften, auch bekannt als Indexer usw. usw. usw.).
Ark-Kun

> "Wie hilft mir" Derived z = new Base () "? BaseCls baseObj; DerivedCls derivativeObj; derivativeObj = (DerivedCls) baseObj "(das Q des OP)?" Es tut es einfach. Es löst die Frage des OP. Und Sie brauchen nicht einmal die explizite Besetzung.
Ark-Kun

> 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, wird specialnamesie 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.
Ark-Kun

VB.Net hat einen Stromversorger. C # nicht. Wie würden Sie einen Stromversorger in C # für die Verwendung in VB.Net überlasten? Definieren Sie einfach eine op_ExponentMethode und markieren Sie sie mit dem specialnameAttribut.
Ark-Kun

0

Wie wäre es mit:

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

0

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.

    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) { }
        }

    }

0

Ich bin nicht der Meinung, dass dies nicht möglich ist. Sie können es so machen:

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;
}

Verwendung:

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

var auto =ist immer noch vom Typsedan
Bendecko

0

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 nullusw. durchführen, aber dies ist die Idee.

 public static DerivedClass ConvertFromBaseToDerived<BaseClass, DerivedClass>(BaseClass baseClass)
            where BaseClass : class, new()
            where DerivedClass : class, BaseClass, new()
        {
            DerivedClass derived = (DerivedClass)Activator.CreateInstance(typeof(DerivedClass));
            derived.GetType().GetFields().ToList().ForEach(field =>
            {
                var base_ = baseClass.GetType().GetField(field.Name).GetValue(baseClass);
                field.SetValue(derived, base_);

            });

            return derived;
        }

0

Sie können das Basisobjekt einfach in JSON serialisieren und dann in das abgeleitete Objekt deserialisieren.



-1

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:

    public static string ConvertToJson<T>(this T obj)
    {
        return JsonConvert.SerializeObject(obj);
    }
    public static T ConvertToObject<T>(this string json)
    {
        if (string.IsNullOrEmpty(json))
        {
            return Activator.CreateInstance<T>();
        }
        return JsonConvert.DeserializeObject<T>(json);
    }

Legen Sie sie für immer in Ihre Toolbox, dann können Sie dies immer tun:

var derivedClass = baseClass.ConvertToJson().ConvertToObject<derivedClass>();

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.


Dies macht nicht das, was OP verlangt hat. Sie erstellen ein neues Objekt des richtigen Typs für die Variable unter Verwendung von Daten aus dem ursprünglichen Objekt des falschen Typs. Dies kann funktionieren oder auch nicht, aber in beiden Fällen wird einer Variablen des abgeleiteten Typs mit Sicherheit kein Objekt des Basisklassentyps zugewiesen.
Lasse V. Karlsen

Ich habe die Frage beantwortet: Ist es möglich, einer abgeleiteten Klassenreferenz mit einem expliziten Typecast ein Basisklassenobjekt zuzuweisen? Indem ich nein sage. Ich biete eine Alternative, die absolut funktioniert und weniger verwirrend ist als Generika. Wie oben mehrfach erwähnt, kann es zu Problemen beim Zuweisen zu einer abgeleiteten Klasseneigenschaft aus einer Basisklasse kommen. Genau so würde es funktionieren (und in apis funktionieren), wenn es möglich wäre. Nur weil meine Antwort von einem "falschen" Typ verwendet werden kann, heißt das nicht, dass sie nicht für einen "richtigen" Typ verwendet werden kann. @ LasseV.Karlsen Bitte ziehen Sie Ihre negative Bewertung zurück.
Patrick Knott

Im Gegensatz zu den meisten Antworten hier, die JsonConverts verketten, zeige ich auch, wie man mit Null umgeht.
Patrick Knott
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.