È possibile assegnare un oggetto di classe base a un riferimento di classe derivato con un typecast esplicito?


89

È possibile assegnare un oggetto di classe base a un riferimento a una classe derivata con un typecast esplicito in C # ?.

L'ho provato e crea un errore di runtime.

Risposte:


100

No. Un riferimento a una classe derivata deve effettivamente fare riferimento a un'istanza della classe derivata (o null). Altrimenti come ti aspetteresti che si comporti?

Per esempio:

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

Se vuoi essere in grado di convertire un'istanza del tipo base nel tipo derivato, ti suggerisco di scrivere un metodo per creare un'istanza del tipo derivato appropriato. Oppure guarda di nuovo il tuo albero dell'eredità e prova a riprogettare in modo da non doverlo fare in primo luogo.


72
@ Mike: il codice si compila bene. Tuttavia cade al momento dell'esecuzione :)
Jon Skeet

1
Allora cosa succede esattamente quando scriviamo Base b = new Derived (); ? Creerà oggetti sia per la classe base che per quella derivata?
Ashif Nataliya

3
@ Akie: No, crea un singolo oggetto di tipo Derived, ma puoi considerare un Derivedriferimento come un Baseriferimento.
Jon Skeet

Quindi c'è qualche differenza nell'oggetto risultante per queste due istruzioni? Base b = nuova Base () e Base b = nuova Derivata ()? qual è il vantaggio di usarne uno rispetto all'altro?
Ashif Nataliya

4
@ Akie: Sì, uno crea un'istanza di Basee l'altro crea un'istanza di Derived. Se chiami un metodo virtuale su bcui è stato eseguito l'override Derived, vedrai il Derivedcomportamento se hai un'istanza di Derived. Ma non è proprio appropriato entrare nei dettagli in un thread di commenti di Stack Overflow: dovresti davvero leggere un buon libro o tutorial C #, poiché si tratta di cose piuttosto fondamentali.
Jon Skeet

47

No, non è possibile poiché assegnarlo a un riferimento a una classe derivata sarebbe come dire "La classe base è un sostituto pienamente capace della classe derivata, può fare tutto ciò che può fare la classe derivata", il che non è vero poiché le classi derivate in generale offrono più funzionalità rispetto alla loro classe base (almeno, questa è l'idea alla base dell'ereditarietà).

È possibile scrivere un costruttore nella classe derivata prendendo un oggetto della classe base come parametro, copiando i valori.

Qualcosa come questo:

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 tal caso, si copierà l'oggetto di base e si otterrà un oggetto di classe derivata completamente funzionale con valori predefiniti per i membri derivati. In questo modo puoi anche evitare il problema segnalato da Jon Skeet:

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

Ho avuto questo problema e l'ho risolto aggiungendo un metodo che accetta un parametro di tipo e converte l'oggetto corrente in quel tipo.

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

Ciò significa che puoi usarlo nel tuo codice in questo modo:

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

È necessario utilizzare il tipo della classe corrente (classe base) per ottenere e impostare le proprietà poiché questi sono i valori che si desidera mappare alla classe derivata.
Bowofola

1
Se hai proprietà che non possono essere scritte nel tipo derivato dovresti probabilmente cambiare in: if (property.CanWrite) property.SetValue (instance, property.GetValue (this, null), null);
user3478586

10

Come molti altri hanno risposto, No.

Uso il seguente codice in quelle sfortunate occasioni in cui ho bisogno di usare un tipo base come tipo derivato. Sì, è una violazione del principio di sostituzione di Liskov (LSP) e sì, la maggior parte delle volte preferiamo la composizione all'eredità. Sostieni Markus Knappen Johansson, su cui si basa la risposta originale.

Questo codice nella classe base:

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

Permette:

    derivedObject = baseObect.As<derivedType>()

Poiché utilizza la riflessione, è "costoso". Usa di conseguenza.


L'ho appena provato e ho pensato che potesse essere migliorato ulteriormente, sovraccaricando l'operatore esplicito (e anche l'operatore implicito) .. ma - il compilatore non lo permette: user-defined conversions to or from a base class are not allowed ne vedo le ragioni, ma sono deluso, poiché sarebbe stato molto divertente se lo avesse permesso ..
Henrik

@ MEC: Ho notato che hai lasciato cadere la parte "where T: MyBaseClass" e hai aggiunto la if (type.BaseType != null)dichiarazione relativa alla A. di Markus Knappen Johansson. Perché? Ciò significa che consentirebbe un tipo nelle chiamate che non è derivato da MyBaseClass (o qualsiasi altra cosa per quella materia). Mi rendo conto che causerà ancora un errore del compilatore se Assegnato a myDerivedObject, ma se è usato solo come espressione, verrà compilato e in fase di esecuzione creerà semplicemente un myDerivedObject senza alcun dato copiato da "myBaseObject". Non riesco a immaginare un caso d'uso per questo.
Tom

@ Tom, risposta in ritardo, ma ho pensato che potesse essere ancora utile. La migliore risposta alla tua domanda probabilmente sarebbe dire che il nome "As" sarebbe stato meglio "AsOrDefault". Essenzialmente possiamo prendere questo risultato e confrontarlo con un Default come facciamo quando usiamo SingleOrDefault o FirstOrDefault di Linq.
MEC

7

No, non è possibile, da qui il tuo errore di runtime.

È tuttavia possibile assegnare un'istanza di una classe derivata a una variabile del tipo di classe di base.


7

Soluzione con JsonConvert (invece di typecast)

Oggi ho affrontato lo stesso problema e ho trovato una soluzione semplice e rapida al problema utilizzando JsonConvert.

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

Ho risposto di nuovo di seguito con metodi di estensione. Sì, questa è la risposta.
Patrick Knott

5

Come tutti qui hanno detto, non è possibile direttamente.

Il metodo che preferisco ed è piuttosto pulito, è usare un Object Mapper come AutoMapper .

Eseguirà il compito di copiare automaticamente le proprietà da un'istanza all'altra (non necessariamente dello stesso tipo).


3

Espandendo la risposta di @ ybo: non è possibile perché l'istanza che hai della classe base non è in realtà un'istanza della classe derivata. Conosce solo i membri della classe base e non sa nulla di quelli della classe derivata.

Il motivo per cui è possibile eseguire il cast di un'istanza della classe derivata su un'istanza della classe base è perché la classe derivata in realtà è già un'istanza della classe base, poiché ha già quei membri. Non si può dire il contrario.


3

È possibile eseguire il cast di una variabile digitata come classe base nel tipo di una classe derivata; tuttavia, per necessità questo eseguirà un controllo in fase di esecuzione, per vedere se l'oggetto effettivo coinvolto è del tipo corretto.

Una volta creato, il tipo di oggetto non può essere modificato (non ultimo, potrebbe non essere della stessa dimensione). Tuttavia, puoi convertire un'istanza, creando una nuova istanza del secondo tipo, ma devi scrivere manualmente il codice di conversione.


2

No, non è possibile.

Si consideri uno scenario in cui un ACBus è una classe derivata della classe base Bus. ACBus ha caratteristiche come TurnOnAC e TurnOffAC che operano su un campo denominato ACState. TurnOnAC imposta ACState su on e TurnOffAC imposta ACState su off. Se provi a utilizzare le funzionalità TurnOnAC e TurnOffAC su Bus, non ha senso.


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

}

quando creiamo un oggetto classe figlio, l'oggetto classe base viene avviato automaticamente in modo che la variabile di riferimento della classe base possa puntare all'oggetto classe figlio.

ma non viceversa perché una variabile di riferimento di una classe figlia non può puntare a un oggetto di classe di base perché non viene creato alcun oggetto di classe figlio.

e notare anche che la variabile di riferimento della classe base può chiamare solo il membro della classe base.


2

In realtà c'è un modo per farlo. Pensa a come potresti utilizzare Newtonsoft JSON per deserializzare un oggetto da json. Ignorerà (o almeno può) gli elementi mancanti e popolerà tutti gli elementi di cui è a conoscenza.

Quindi ecco come l'ho fatto. Un piccolo esempio di codice seguirà la mia spiegazione.

  1. Crea un'istanza del tuo oggetto dalla classe base e popolala di conseguenza.

  2. Utilizzando la classe "jsonconvert" di Newtonsoft json, serializzare quell'oggetto in una stringa json.

  3. Ora crea il tuo oggetto di sottoclasse deserializzandolo con la stringa json creata nel passaggio 2. Questo creerà un'istanza della tua sottoclasse con tutte le proprietà della classe di base.

Funziona a meraviglia! Allora .. quando è utile? Alcune persone hanno chiesto quando questo avrebbe senso e hanno suggerito di modificare lo schema dell'OP per accogliere il fatto che non è possibile farlo in modo nativo con l'ereditarietà delle classi (in .Net).

Nel mio caso, ho una classe di impostazioni che contiene tutte le impostazioni "di base" per un servizio. I servizi specifici hanno più opzioni e quelli provengono da una tabella DB diversa, quindi quelle classi ereditano la classe di base. Hanno tutti un diverso insieme di opzioni. Quindi, quando si recuperano i dati per un servizio, è molto più semplice popolare PRIMA i valori utilizzando un'istanza dell'oggetto di base. Un metodo per farlo con una singola query DB. Subito dopo, creo l'oggetto della sottoclasse usando il metodo descritto sopra. Quindi effettuo una seconda query e popolo tutti i valori dinamici sull'oggetto della sottoclasse.

L'output finale è una classe derivata con tutte le opzioni impostate. La ripetizione per ulteriori nuove sottoclassi richiede solo poche righe di codice. È semplice e utilizza un pacchetto molto collaudato (Newtonsoft) per far funzionare la magia.

Questo codice di esempio è vb.Net, ma puoi convertirlo facilmente in c #.

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

utilizzando C # e Newtonsoft.Json: var destObject = JsonConvert.DeserializeObject<DestinationType>(JsonConvert.SerializeObject(srcObject));. Lo userei solo per unit test e altri "hacking" non di produzione!
thinkOfaNumber

2

Puoi usare un'estensione:

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

Nel codice:

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

Potrebbe non essere pertinente, ma sono stato in grado di eseguire codice su un oggetto derivato data la sua base. È decisamente più hacky di quanto vorrei, ma funziona:

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

Puoi farlo usando generic.

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

Ottieni tre vantaggi utilizzando questo approccio.

  1. Non stai duplicando il codice
  2. Non stai usando la riflessione (che è lenta)
  3. Tutte le tue conversioni sono in un unico posto

1

So che è vecchio ma l'ho usato con successo per un bel po '.

   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

Ho combinato alcune parti delle risposte precedenti (grazie a quegli autori) e ho messo insieme una semplice classe statica con due metodi che stiamo usando.

Sì, è semplice, no non copre tutti gli scenari, sì, potrebbe essere ampliato e migliorato, no non è perfetto, sì potrebbe essere reso più efficiente, no non è la cosa più grande dai tempi del pane a fette, sì ci sono mappatori di oggetti di pacchetti nuget completi e robusti là fuori che sono molto migliori per un uso intensivo, ecc. ecc.

E ovviamente proverà a mappare i valori da qualsiasi oggetto a qualsiasi oggetto, derivato o meno (solo le proprietà pubbliche che hanno lo stesso nome, ovviamente, ignorano il resto).

UTILIZZO:

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

CLASSE DI UTILITÀ STATICA:

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

È possibile utilizzare un costruttore di copie che richiami immediatamente il costruttore di istanze oppure, se il costruttore di istanze fa più delle assegnazioni, il costruttore di copie assegna i valori in entrata all'istanza.

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

Ha fatto riferimento alla documentazione di Microsoft C # in Constructor per questo esempio che ha avuto questo problema in passato.


0

Un'altra soluzione è aggiungere un metodo di estensione in questo modo:

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

quindi avere un costruttore in ogni classe derivata che accetta la classe base:

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

Opzionalmente sovrascriverà anche le proprietà di destinazione se già impostate (non nulle) o meno.


0

È possibile assegnare un oggetto di classe base a un riferimento a una classe derivata con un typecast esplicito in C # ?.

Sono possibili non solo conversioni esplicite, ma anche implicite.

Il linguaggio C # non consente tali operatori di conversione, ma puoi comunque scriverli usando il C # puro e funzionano. Si noti che la classe che definisce l'operatore di conversione implicito ( Derived) e la classe che utilizza l'operatore ( Program) devono essere definite in assembly separati (ad esempio, la Derivedclasse è in a library.dllcui si fa riferimento program.execontenendo la Programclasse).

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

Quando si fa riferimento alla libreria utilizzando il riferimento al progetto in Visual Studio, VS mostra scarabocchi quando si utilizza la conversione implicita, ma viene compilato correttamente. Se fai riferimento solo a library.dll, non ci sono scarabocchi.


Che magia nera è questa?!? Inoltre, in che modo "Derived z = new Base ()" mi aiuta a fare "BaseCls baseObj; DerivedCls derivatoObj; derivatoObj = (DerivedCls) baseObj" (la Q dell'OP)? Inoltre, cosa fa System.Runtime.CompilerServices.SpecialNameAttribute? I documenti per ogni versione dalla prima disponibile (2.0) alla "versione corrente" (4.6? "Chiunque? Chiunque?") Non dicono cosa fa, ma dicono "La classe SpecialNameAttribute non è attualmente utilizzata in .NET Framework, ma è riservato per un utilizzo futuro. ". Vedi: [link] ( msdn.microsoft.com/en-us/library/ms146064(v=vs.100).aspx ).
Tom

> "Che magia nera è questa?!?" Si chiama .Net Framework (CLR, IL, BCL). Il set di funzionalità dei linguaggi IL, C # e VB non sono gli stessi. Ci sono funzionalità in VB che C # non supporta. Ci sono funzionalità in IL che C # non supporta. Ci sono restrizioni in C # che sono piuttosto arbitrarie e non esistono where T : Delegatenell'IL sottostante ( proprietà simili o parametrizzate, ovvero indicizzatori, ecc., Ecc.).
Ark-kun

> "Inoltre, in che modo" Derived z = new Base () "mi aiuta a fare" BaseCls baseObj; DerivedCls derivatoObj; DerivedObj = (DerivedCls) baseObj "(la Q dell'OP)?" Lo fa e basta. Risolve la domanda dell'OP. E non hai nemmeno bisogno del cast esplicito.
Ark-kun

> what does System.Runtime.CompilerServices.SpecialName Attribute do?- Viene utilizzato per contrassegnare i metodi prodotti da alcuni costrutti di convenienza speciali dei linguaggi .Net di alto livello: funzioni di accesso alle proprietà, funzioni di accesso agli eventi, costruttori, operatori, indicizzatori, ecc. A meno che il metodo IL non sia contrassegnato con specialnamenon verrebbe visualizzato come proprietà / evento / costruttore e sarebbe semplicemente riconosciuto come un metodo normale. Contrassegnare manualmente i metodi con nome appropriato con questo attributo è solo eseguire manualmente una parte del lavoro del compilatore.
Ark-kun

VB.Net dispone di operatore di alimentazione. C # no. Come sovraccaricheresti un operatore di alimentazione in C # per utilizzarlo in VB.Net? Basta definire un op_Exponentmetodo e contrassegnarlo con l' specialnameattributo.
Ark-kun

0

Che ne dite di:

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

0

Il modo migliore per aggiungere tutte le proprietà di base all'elemento derivato è utilizzare la riflessione nel costruttore. Prova questo codice, senza creare metodi o istanze.

    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

Non sono d'accordo che non sia possibile. Puoi farlo in questo modo:

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

Utilizzo:

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

var auto =è ancora di tiposedan
bendecko

0

È così che ho risolto questo problema per i campi. Puoi fare la stessa iterazione attraverso le proprietà, se lo desideri. Potresti voler fare alcuni controlli per nullecc. Ma questa è l'idea.

 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

Puoi semplicemente serializzare l'oggetto di base in JSON e quindi deserializzarlo nell'oggetto derivato.


0

Non nel senso tradizionale ... Converti in Json, poi nel tuo oggetto e boom, fatto! Jesse sopra aveva pubblicato la risposta per primo, ma non ha utilizzato questi metodi di estensione che rendono il processo molto più semplice. Crea un paio di metodi di estensione:

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

Mettili nella tua cassetta degli attrezzi per sempre, quindi puoi sempre farlo:

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

Ah, il potere di JSON.

Ci sono un paio di trucchi con questo approccio: stiamo davvero creando un nuovo oggetto, non il casting, che può o non può avere importanza. I campi privati ​​non verranno trasferiti, i costruttori con parametri non verranno chiamati, ecc. È possibile che qualche json figlio non venga assegnato. I flussi non sono gestiti in modo innato da JsonConvert. Tuttavia, se la nostra classe non si basa su campi e costruttori privati, questo è un metodo molto efficace per spostare i dati da una classe all'altra senza mappare e chiamare i costruttori, che è il motivo principale per cui vogliamo eseguire il cast in primo luogo.


Questo non fa quello che ha chiesto OP. Quello che stai facendo è costruire un nuovo oggetto del tipo corretto per la variabile, utilizzando i dati dell'oggetto originale del tipo sbagliato. Questo può o non può funzionare, ma in entrambi i casi, non è certamente assegnare un oggetto del tipo di classe base a una variabile del tipo derivato.
Lasse V. Karlsen,

Ho risposto alla domanda: è possibile assegnare un oggetto di classe base a un riferimento di classe derivato con un typecast esplicito? Dicendo no. Sto fornendo un'alternativa che funziona assolutamente ed è meno confusa dei generici. Come indicato numerose volte sopra, può causare problemi nell'assegnazione di proprietà di una classe derivata da una classe base, tuttavia, questo è esattamente il modo in cui funzionerebbe (e funziona in api) se fosse possibile. Solo perché la mia risposta può essere usata da un tipo "sbagliato" non significa che non possa essere usata per un tipo "giusto". @ LasseV.Karlsen per favore ritira la tua valutazione negativa.
Patrick Knott

A differenza della maggior parte delle risposte qui che collegano a margherita JsonConverts, mostro come gestire anche null.
Patrick Knott

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.