¿Es posible asignar un objeto de clase base a una referencia de clase derivada con un encasillado explícito?

88

¿Es posible asignar un objeto de clase base a una referencia de clase derivada con un encasillado explícito en C #?

Lo he probado y crea un error en tiempo de ejecución.

Maddy.Shik
fuente

Respuestas:

98

No. Una referencia a una clase derivada debe referirse realmente a una instancia de la clase derivada (o nula). De lo contrario, ¿cómo esperaría que se comporte?

Por ejemplo:

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

Si desea poder convertir una instancia del tipo base en el tipo derivado, le sugiero que escriba un método para crear una instancia de tipo derivado adecuada. O mire su árbol de herencia nuevamente e intente rediseñarlo para que no tenga que hacer esto en primer lugar.

Jon Skeet
fuente
72
@ Mike: El código se compila muy bien. Sin embargo, se cae en el momento de la ejecución :)
Jon Skeet
1
Entonces, ¿qué sucede exactamente cuando escribimos Base b = new Derived (); ? ¿Creará objetos para la clase base y derivada?
Ashif Nataliya
3
@Akie: No, crea un único objeto de tipo Derived, pero puede tratar una Derivedreferencia como Basereferencia.
Jon Skeet
Entonces, ¿hay alguna diferencia en el objeto resultante para estas dos declaraciones? Base b = nueva Base () y Base b = nueva Derivada ()? ¿Cuál es el beneficio de usar uno sobre otro?
Ashif Nataliya
4
@Akie: Sí, uno crea una instancia de Basey el otro crea una instancia de Derived. Si llama a un método virtual en el bque se anuló Derived, verá el Derivedcomportamiento si tiene una instancia de Derived. Pero no es realmente apropiado entrar en detalles en un hilo de comentarios de Stack Overflow; realmente debería leer un buen libro o tutorial de C #, ya que esto es algo bastante fundamental.
Jon Skeet
46

No, eso no es posible ya que asignarlo a una referencia de clase derivada sería como decir "La clase base es un sustituto totalmente capaz de la clase derivada, puede hacer todo lo que puede hacer la clase derivada", lo cual no es cierto ya que las clases derivadas en general ofrecen más funcionalidad que su clase base (al menos, esa es la idea detrás de la herencia).

Podría escribir un constructor en la clase derivada tomando un objeto de clase base como parámetro, copiando los valores.

Algo como esto:

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

En ese caso, copiaría el objeto base y obtendría un objeto de clase derivada completamente funcional con valores predeterminados para miembros derivados. De esta forma también puedes evitar el problema señalado por 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!
Michael Klement
fuente
23

Tuve este problema y lo resolví agregando un método que toma un parámetro de tipo y convierte el objeto actual en ese 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;
}

Eso significa que puede usarlo en su código de esta manera:

var base = new Base();
base.Data = 1;
var derived = base.As<Derived>();
Console.Write(derived.Data); // Would output 1
Markus Knappen Johansson
fuente
Debe usar el tipo de la clase actual (clase base) para obtener y establecer propiedades, ya que esos son los valores que desea asignar a la clase derivada.
Bowofola
1
Si tiene propiedades en las que no se puede escribir en el tipo derivado, probablemente debería cambiar a: if (property.CanWrite) property.SetValue (instancia, propiedad.GetValue (this, null), null);
user3478586
10

Como muchos otros han respondido, No.

Utilizo el siguiente código en esas desafortunadas ocasiones en las que necesito usar un tipo base como tipo derivado. Sí, es una violación del principio de sustitución de Liskov (LSP) y sí, la mayoría de las veces favorecemos la composición sobre la herencia. Apoyos para Markus Knappen Johansson, cuya respuesta original se basa en esto.

Este código en la clase 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;
    }

Permite:

    derivedObject = baseObect.As<derivedType>()

Dado que utiliza la reflexión, es "caro". Utilice en consecuencia.

MEC
fuente
Intenté esto, y pensé que podría mejorarse aún más, sobrecargando el operador explícito (y también el operador implícito) ... pero, el compilador no lo permitirá: user-defined conversions to or from a base class are not allowed veo las razones de esto, pero estoy decepcionado, ya que hubiera sido muy divertido si permitiera esto ..
Henrik
@MEC: Me di cuenta de que eliminó la parte `where T: MyBaseClass` y agregó la if (type.BaseType != null)declaración relativa a la A. de Markus Knappen Johansson. Eso significa que permitiría un Tipo en las Llamadas que no se Derive de MyBaseClass (ni nada para el caso). Me doy cuenta de que seguirá causando un error de compilación si está asignado a myDerivedObject, pero si solo se usa como una expresión, se compilará y, en tiempo de ejecución, simplemente creará un myDerivedObject sin ningún dato copiado de "myBaseObject". No puedo imaginar un caso de uso para eso.
Tom
@Tom, respuesta tardía, pero pensé que aún podría ser útil. La mejor respuesta a su pregunta probablemente sería decir que el nombre "Como" hubiera sido mejor "AsOrDefault". Esencialmente, podemos tomar este resultado y compararlo con un valor predeterminado, como lo hacemos cuando usamos SingleOrDefault o FirstOrDefault de Linq.
MEC
7

No, no es posible, de ahí su error de tiempo de ejecución.

Pero puede asignar una instancia de una clase derivada a una variable de tipo de clase base.

ybo
fuente
7

Solución con JsonConvert (en lugar de encasillado)

Hoy enfrenté el mismo problema y encontré una solución simple y rápida al problema usando JsonConvert.

var base = new BaseClass();
var json = JsonConvert.SerializeObject(base);
DerivedClass derived = JsonConvert.DeserializeObject<DerivedClass>(json);
Jesse de gans
fuente
Respondí esto nuevamente a continuación con métodos de extensión. Si, esta es la respuesta.
Patrick Knott
5

Como dijeron todos aquí, eso no es posible directamente.

El método que prefiero y es bastante limpio, es usar un Object Mapper como AutoMapper .

Hará la tarea de copiar propiedades de una instancia a otra (no necesariamente del mismo tipo) automáticamente.

Mahmoodvcs
fuente
3

Ampliando la respuesta de @ ybo, no es posible porque la instancia que tiene de la clase base no es en realidad una instancia de la clase derivada. Solo conoce los miembros de la clase base y no sabe nada sobre los de la clase derivada.

La razón por la que puede convertir una instancia de la clase derivada a una instancia de la clase base es porque la clase derivada ya es una instancia de la clase base, ya que ya tiene esos miembros. No se puede decir lo contrario.

Andy
fuente
3

Puede convertir una variable que se escribe como la clase base al tipo de una clase derivada; sin embargo, esto necesariamente hará una verificación en tiempo de ejecución, para ver si el objeto real involucrado es del tipo correcto.

Una vez creado, el tipo de objeto no se puede cambiar (sobre todo, es posible que no tenga el mismo tamaño). Sin embargo, puede convertir una instancia, creando una nueva instancia del segundo tipo, pero debe escribir el código de conversión manualmente.

Marc Gravell
fuente
2

No, no es posible.

Considere un escenario en el que un ACBus es una clase derivada de un bus de clase base. ACBus tiene características como TurnOnAC y TurnOffAC que operan en un campo llamado ACState. TurnOnAC activa ACState y TurnOffAC desactiva ACState. Si intenta utilizar las funciones TurnOnAC y TurnOffAC en Bus, no tiene sentido.

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

}

cuando creamos un objeto de clase secundaria, el objeto de clase base se inicia automáticamente para que la variable de referencia de la clase base pueda apuntar al objeto de clase secundaria.

pero no al revés porque una variable de referencia de clase secundaria no puede apuntar al objeto de clase base porque no se crea ningún objeto de clase secundaria.

y también observe que la variable de referencia de la clase base solo puede llamar al miembro de la clase base.

shatrudhan kumar
fuente
2

De hecho, HAY una forma de hacer esto. Piense en cómo podría usar Newtonsoft JSON para deserializar un objeto de json. Ignorará (o al menos podrá) los elementos que faltan y completará todos los elementos que conozca.

Así que así es como lo hice. Una pequeña muestra de código seguirá mi explicación.

  1. Cree una instancia de su objeto a partir de la clase base y rellénela en consecuencia.

  2. Usando la clase "jsonconvert" de Newtonsoft json, serialice ese objeto en una cadena json.

  3. Ahora cree su objeto de subclase deserializando con la cadena json creada en el paso 2. Esto creará una instancia de su subclase con todas las propiedades de la clase base.

¡Esto funciona de maravilla! Entonces ... ¿cuándo es esto útil? Algunas personas preguntaron cuándo tendría sentido esto y sugirieron cambiar el esquema del OP para adaptarse al hecho de que no puede hacer esto de forma nativa con la herencia de clases (en .Net).

En mi caso, tengo una clase de configuración que contiene todas las configuraciones "base" para un servicio. Los servicios específicos tienen más opciones y provienen de una tabla de base de datos diferente, por lo que esas clases heredan la clase base. Todos tienen un conjunto diferente de opciones. Por tanto, al recuperar los datos de un servicio, es mucho más fácil rellenar PRIMERO los valores utilizando una instancia del objeto base. Un método para hacer esto con una sola consulta de base de datos. Inmediatamente después de eso, creo el objeto de la subclase usando el método descrito anteriormente. Luego hago una segunda consulta y completo todos los valores dinámicos en el objeto de la subclase.

El resultado final es una clase derivada con todas las opciones establecidas. Repetir esto para nuevas subclases adicionales requiere solo unas pocas líneas de código. Es simple y usa un paquete muy probado (Newtonsoft) para hacer que la magia funcione.

Este código de ejemplo es vb.Net, pero puede convertirlo fácilmente a 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)
Mark Sauer
fuente
usando C # y Newtonsoft.Json: var destObject = JsonConvert.DeserializeObject<DestinationType>(JsonConvert.SerializeObject(srcObject));. ¡Solo usaría esto para pruebas unitarias y otros "piratería" que no sean de producción!
thinkOfaNumber
2

Puede utilizar una extensión:

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

En codigo:

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

}
Ricardo Figueiredo
fuente
1

Puede que no sea relevante, pero pude ejecutar código en un objeto derivado dada su base. Definitivamente es más hacky de lo que me gustaría, pero funciona:

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);
tstone2077
fuente
1

Puede hacer esto 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; }
}

Obtiene tres beneficios con este enfoque.

  1. No estás duplicando el código
  2. No estás usando la reflexión (que es lenta)
  3. Todas sus conversiones están en un solo lugar
adeel41
fuente
1

Sé que esto es antiguo, pero lo he usado con éxito durante bastante tiempo.

   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);
        }
    } 
Chris
fuente
1

Combiné algunas partes de las respuestas anteriores (gracias a esos autores) y armé una clase estática simple con dos métodos que estamos usando.

Sí, es simple, no, no cubre todos los escenarios, sí, podría expandirse y mejorarse, no, no es perfecto, sí, posiblemente podría hacerse más eficiente, no, no es lo mejor desde el pan de molde, sí, los hay mapeadores de objetos de paquetes nuget robustos y completos que son mucho mejores para un uso intensivo, etc., etc., yada yada, pero funciona para nuestras necesidades básicas :)

Y, por supuesto, intentará mapear valores de cualquier objeto a cualquier objeto, derivado o no (solo las propiedades públicas que tienen el mismo nombre, por supuesto, ignora el resto).

USO:

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

CLASE DE UTILIDAD ESTÁTICA:

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);
            }
        });
    }
}
AVH
fuente
1

Puede usar un constructor de copia que invoca inmediatamente al constructor de instancia, o si su constructor de instancia hace más que asignaciones, el constructor de copia asigna los valores entrantes a la instancia.

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

Se hizo referencia a la documentación de Microsoft C # en Constructor para este ejemplo después de haber tenido este problema en el pasado.

mtpultz
fuente
0

Otra solución es agregar un método de extensión así:

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

luego tenga un constructor en cada clase derivada que acepte la clase base:

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

También sobrescribirá opcionalmente las propiedades de destino si ya están configuradas (no nulas) o no.

d.popov
fuente
0

¿Es posible asignar un objeto de clase base a una referencia de clase derivada con un encasillado explícito en C #?

No solo son posibles conversiones explícitas, sino también implícitas.

El lenguaje C # no permite tales operadores de conversión, pero aún puede escribirlos usando C # puro y funcionan. Tenga en cuenta que la clase que define el operador de conversión implícito ( Derived) y la clase que usa el operador ( Program) deben definirse en ensamblajes separados (por ejemplo, la Derivedclase está en una a la library.dllque se hace referencia al program.execontener la Programclase).

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

Cuando hace referencia a la biblioteca usando la Referencia del proyecto en Visual Studio, VS muestra garabatos cuando usa la conversión implícita, pero se compila bien. Si solo hace referencia a library.dll, no hay garabatos.

Ark-kun
fuente
¿Qué magia negra es esta?!? Además, ¿cómo me ayuda "Derived z = new Base ()" a hacer "BaseCls baseObj; DerivedCls derivadoObj; derivadoObj = (DerivedCls) baseObj" (el OP's Q)? Además, ¿qué hace System.Runtime.CompilerServices.SpecialNameAttribute? Los documentos de cada versión, desde la más antigua disponible (2.0) hasta la "versión actual" (4.6? "¿Alguien? ¿Alguien?") No dicen lo que hace, pero sí dicen "La clase SpecialNameAttribute no se usa actualmente en .NET Framework, pero está reservado para uso futuro. ". Ver: [enlace] ( msdn.microsoft.com/en-us/library/ms146064(v=vs.100).aspx ).
Tom
> "¿Qué magia negra es esta?!?" Eso se llama .Net Framework (CLR, IL, BCL). El conjunto de características de los lenguajes IL, C # y VB no es el mismo. Hay funciones en VB que C # no admite. Hay funciones en IL que C # no admite. Hay restricciones en C # que son bastante arbitrarias y no existen en IL subyacente ( where T : Delegatepropiedades similares o parametrizadas, también conocidas como indexadores, etc., etc.).
Ark-kun
> "Además, ¿cómo me ayuda" Derived z = new Base () "a" BaseCls baseObj; DerivedCls derivadoObj; DerivedObj = (DerivedCls) baseObj "(la Q del OP)? Simplemente lo hace. Resuelve la pregunta del OP. Y ni siquiera necesitas el elenco explícito.
Ark-kun
> what does System.Runtime.CompilerServices.SpecialName Attribute do?- Se usa para marcar los métodos producidos por algunas construcciones especiales de conveniencia de los lenguajes .Net de alto nivel: accesores de propiedad, accesores de eventos, constructores, operadores, indexadores, etc. A menos que el método IL esté marcado con specialname, no se vería como propiedad / evento / constructor y sería reconocido como un método normal. Marcar manualmente métodos con nombres apropiados con este atributo es simplemente hacer manualmente un poco del trabajo del compilador.
Ark-kun
VB.Net tiene operador de energía. C # no lo hace. ¿Cómo sobrecargaría un operador de energía en C # para usarlo en VB.Net? Simplemente defina un op_Exponentmétodo y márquelo con el specialnameatributo.
Ark-kun
0

Qué tal si:

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

La mejor manera de agregar todas las propiedades base al elemento derivado es usar la reflexión en costructor. Pruebe este código, sin crear métodos o instancias.

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

    }
Uraitz
fuente
0

No estoy de acuerdo con que no sea posible. Puedes hacerlo así:

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

Uso:

var sedan = new Sedan();
sedan.NumberOfDoors = 4;
var auto = ConvertAuto<Auto>(sedan);
Buzz Wilder
fuente
var auto =sigue siendo de tiposedan
bendecko
0

Así es como resolví esto para los campos. Puede hacer la misma iteración a través de las propiedades si lo desea. Es posible que desee hacer algunas comprobaciones para, nulletc., pero esta es la 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;
        }
Tamás Panyi
fuente
0

Puede simplemente serializar el objeto base en JSON y luego deserializarlo en el objeto derivado.

Metan Patel
fuente
0

No en el sentido tradicional ... Convierta a Json, luego a su objeto, ¡y listo! Jesse publicó primero la respuesta, pero no usó estos métodos de extensión que facilitan mucho el proceso. Crea un par de métodos de extensión:

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

Ponlos en tu caja de herramientas para siempre, entonces siempre podrás hacer esto:

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

Ah, el poder de JSON.

Hay un par de errores con este enfoque: Realmente estamos creando un nuevo objeto, no lanzando, lo que puede o no importar. Los campos privados no serán transferidos, los constructores con parámetros no serán llamados, etc. Es posible que no se asigne algún json hijo. JsonConvert no gestiona las transmisiones de forma innata. Sin embargo, si nuestra clase no se basa en campos y constructores privados, este es un método muy efectivo para mover datos de una clase a otra sin mapear y llamar a constructores, que es la razón principal por la que queremos lanzar en primer lugar.

Patrick Knott
fuente
Esto no hace lo que pidió OP. Lo que está haciendo es construir un nuevo objeto del tipo correcto para la variable, utilizando datos del objeto original del tipo incorrecto. Esto puede funcionar o no, pero de cualquier manera, ciertamente no está asignando un objeto del tipo de clase base a una variable del tipo derivado.
Lasse V. Karlsen
Respondí a la pregunta: ¿Es posible asignar un objeto de clase base a una referencia de clase derivada con un encasillado explícito? Diciendo que no. Estoy proporcionando una alternativa que funciona absolutamente y es menos confusa que los genéricos. Como se ha indicado en numerosas ocasiones anteriormente, puede causar problemas al asignar propiedades a una clase derivada de una clase base, sin embargo, así es exactamente como funcionaría (y lo hace en apis) si fuera posible. El hecho de que mi respuesta pueda usarse de un tipo "incorrecto" no significa que no pueda usarse para un tipo "correcto". @ LasseV.Karlsen retracte su calificación negativa.
Patrick Knott
A diferencia de la mayoría de las respuestas aquí que encadenan JsonConverts, también muestro cómo manejar null.
Patrick Knott
-1

No, vea esta pregunta que hice: Upcasting en .NET usando genéricos

La mejor manera es hacer un constructor predeterminado en la clase, construir y luego llamar a un Initialisemétodo

Aaron Powell
fuente