No se puede transmitir de la clase para padres a la clase para niños

Estoy intentando convertir de una clase para padres a una clase para niños, pero obtengo una InvalidCastException. La clase secundaria solo tiene una propiedad de tipo int. ¿Alguien sabe lo que tengo que hacer?

No puedes convertir a un mamífero en un perro; puede ser un gato.

No puede echar comida en un sándwich, puede ser una hamburguesa con queso.

No se puede lanzar un automóvil en un Ferrari – podría ser un Honda, o más específicamente, no se puede lanzar un Ferrari 360 Modena a un Ferrari 360 Challange Stradale – hay diferentes partes, a pesar de que ambos son Ferrari 360s.

Una forma simple de downcast en C # es serializar el padre y luego deserializarlo en el niño.

var serializedParent = JsonConvert.SerializeObject(parentInstance); Child c = JsonConvert.DeserializeObject(serializedParent); 

Tengo una aplicación de consola simple que arroja animales en el perro, utilizando las dos líneas de código anteriores aquí

La instancia a la que se refiere su referencia de clase base no es una instancia de su clase secundaria. No hay nada malo.

Más específicamente:

 Base derivedInstance = new Derived(); Base baseInstance = new Base(); Derived good = (Derived)derivedInstance; // OK Derived fail = (Derived)baseInstance; // Throws InvalidCastException 

Para que el reparto sea exitoso, la instancia en la que estás realizando un downcasting debe ser una instancia de la clase a la que estás realizando downcasting (o al menos, la clase a la que estás realizando downcasting debe estar dentro de la jerarquía de clases de la instancia); el lanzamiento fallará

Hay algunos casos en los que tal elenco tendría sentido.
En mi caso, estaba recibiendo una clase BASE a través de la red, y necesitaba más funciones para ello. Así que derivarlo para manejarlo de mi lado con todas las campanas y silbatos que quería, y convertir la clase BASE recibida en el DERIVADO simplemente no era una opción (lanza InvalidCastException of Course)

Una SOLUCIÓN práctica pensada fue la de declarar una clase EXTENSION Helper que NO heredaba realmente la clase BASE, sino que INCLUYE IT como miembro.

 public class BaseExtension { Base baseInstance; public FakeDerived(Base b) { baseInstance = b; } //Helper methods and extensions to Base class added here } 

Si tiene acoplamiento flexible y solo necesita un par de características adicionales para la clase base sin que REALMENTE tenga una necesidad absoluta de derivación, esa podría ser una solución rápida y sencilla.

Eso violaría los principios orientados a objetos. Yo diría que una solución elegante aquí y en cualquier otro lugar del proyecto es utilizar un marco de mapeo de objetos como AutoMapper para configurar una proyección.

Aquí hay una configuración ligeramente más compleja que la necesaria, pero es lo suficientemente flexible para la mayoría de los casos:

 public class BaseToChildMappingProfile : Profile { public override string ProfileName { get { return "BaseToChildMappingProfile"; } } protected override void Configure() { Mapper.CreateMap(); Mapper.CreateMap(); } } public class AutoMapperConfiguration { public static void Configure() { Mapper.Initialize(x => { x.AddProfile(); }); } } 

Cuando se inicia la aplicación, llame a AutoMapperConfiguration.Configure() y luego puede proyectar de esta manera:

 ChildClassOne child = Mapper.Map(baseClass); 

Las propiedades se asignan por convención, por lo que si la clase se hereda, los nombres de propiedad son exactamente los mismos y la asignación se configura automáticamente. Puede agregar propiedades adicionales modificando la configuración. Ver la documentación .

Paul, no preguntaste ‘¿Puedo hacerlo? – ¡Asumo que quieres saber cómo hacerlo!

Tuvimos que hacer esto en un proyecto: hay muchas clases que configuramos de forma genérica solo una vez, luego inicializamos las propiedades específicas de las clases derivadas. Utilizo VB para que mi muestra esté en VB (noogies difíciles), pero robé la muestra de VB de este sitio que también tiene una mejor versión de C #:

http://www.eggheadcafe.com/tutorials/aspnet/a4264125-fcb0-4757-9d78-ff541dfbcb56/net-reflection–copy-cl.aspx

Código de muestra:

 Imports System Imports System.Collections.Generic Imports System.Reflection Imports System.Text Imports System.Diagnostics Module ClassUtils Public Sub CopyProperties(ByVal dst As Object, ByVal src As Object) Dim srcProperties() As PropertyInfo = src.GetType.GetProperties Dim dstType = dst.GetType If srcProperties Is Nothing Or dstType.GetProperties Is Nothing Then Return End If For Each srcProperty As PropertyInfo In srcProperties Dim dstProperty As PropertyInfo = dstType.GetProperty(srcProperty.Name) If dstProperty IsNot Nothing Then If dstProperty.PropertyType.IsAssignableFrom(srcProperty.PropertyType) = True Then dstProperty.SetValue(dst, srcProperty.GetValue(src, Nothing), Nothing) End If End If Next End Sub End Module Module Module1 Class base_class Dim _bval As Integer Public Property bval() As Integer Get Return _bval End Get Set(ByVal value As Integer) _bval = value End Set End Property End Class Class derived_class Inherits base_class Public _dval As Integer Public Property dval() As Integer Get Return _dval End Get Set(ByVal value As Integer) _dval = value End Set End Property End Class Sub Main() ' NARROWING CONVERSION TEST Dim b As New base_class b.bval = 10 Dim d As derived_class 'd = CType(b, derived_class) ' invalidcast exception 'd = DirectCast(b, derived_class) ' invalidcast exception 'd = TryCast(b, derived_class) ' returns 'nothing' for c d = New derived_class CopyProperties(d, b) d.dval = 20 Console.WriteLine(b.bval) Console.WriteLine(d.bval) Console.WriteLine(d.dval) Console.ReadLine() End Sub End Module 

Por supuesto, esto no es realmente un casting. Está creando un nuevo objeto derivado y copiando las propiedades del elemento primario, dejando las propiedades secundarias en blanco. Eso es todo lo que necesitaba hacer y parece que es todo lo que necesitas hacer. Tenga en cuenta que solo copia las propiedades, no los miembros (variables públicas) en la clase (pero podría ampliarlo para hacer eso si es por vergüenza al exponer a los miembros públicos).

Casting en general crea 2 variables que apuntan al mismo objeto (mini tutorial aquí, por favor no me arroje excepciones de casos de esquina). ¡Esto tiene ramificaciones significativas (ejercicio para el lector)!

Por supuesto, tengo que decir por qué el languague no te permite ir desde la base para derivar una instancia, sino que lo hace de la otra manera. Imagine un caso en el que puede tomar una instancia de un cuadro de texto de winforms (derivado) y almacenarlo en una variable del tipo control de Winforms. Por supuesto, el “control” puede mover el objeto alrededor de OK y usted puede manejar todas las cosas de “control-y” sobre el cuadro de texto (por ejemplo, arriba, izquierda, propiedades de texto). Las cosas específicas del cuadro de texto (por ej., .Multiline) no se pueden ver sin enviar la variable de tipo ‘control’ apuntando al cuadro de texto en la memoria, pero aún está allí en la memoria.

Ahora imagine, usted tiene un control y desea incluir una variable de tipo cuadro de texto en él. El control en la memoria no tiene ‘multilínea’ y otras cosas textboxboxy. Si intenta hacer referencia a ellos, ¡el control no hará crecer mágicamente una propiedad multilínea! La propiedad (mírela como una variable miembro aquí, que en realidad almacena un valor, porque está activa en la memoria de la instancia del cuadro de texto) debe existir. Como estás lanzando, recuerda que tiene que ser el mismo objeto al que apunta. Por lo tanto, no es una restricción de idioma, es filosóficamente imposible argumentar de esa manera.

He visto a la mayoría de las personas decir que el casting explícito de padres a hijos no es posible, que en realidad no es cierto. Tomemos un comienzo revisado e intentemos probarlo con ejemplos.

Como sabemos en .net, todas las piezas moldeadas tienen dos categorías amplias.

  1. Para el tipo de valor
  2. Para el tipo de referencia (en su caso, su tipo de referencia)

El tipo de referencia tiene otros tres casos situacionales principales en los que puede encontrarse cualquier escenario.

De niño a padre (Casting implícito – Siempre exitoso)

Caso 1. Hijo a cualquier padre directo o indirecto

 Employee e = new Employee(); Person p = (Person)e; //Allowed 

De padres a hijos (lanzamiento explícito: puede ser exitoso)

Caso 2. Objeto matriz que contiene el objeto principal (no permitido)

 Person p = new Person(); // p is true Person object Employee e = (Employee)p; //Runtime err : InvalidCastException <-------- Yours issue 

Caso 3. Variable principal que contiene el objeto hijo (Siempre exitoso)

Nota: Debido a que los objetos tienen naturaleza polimórfica, es posible que una variable de un tipo de clase principal tenga un tipo secundario.

 Person p = new Employee(); // p actually is Employee Employee e = (Employee)p; // Casting allowed 

Conclusión: después de leer sobre todo, espero que tenga sentido ahora, así como la conversión de padres a hijos es posible (Caso 3).

Respuesta a la pregunta :

Su respuesta está en el caso 2. Cuando vea que la conversión no está permitida por OOP y está tratando de violar una de las reglas básicas de OOP. Por lo tanto, elija una ruta segura.

Además, para evitar tales situaciones excepcionales, .net ha recomendado el uso de / es operadores que le ayudarán a tomar decisiones informadas y proporcionar un casting seguro.

La instancia del objeto se debe crear utilizando el tipo de clase secundaria, no se puede convertir una instancia de tipo principal a un tipo secundario

A partir de C # 7.0, puede usar la palabra clave es para hacer esto:

Con esa clase definida:

 class Base { /* Define base class */ } class Derived : Base { /* Define derived class */ } 

A continuación, puede hacer algo como:

 void Funtion(Base b) { if (b is Derived d) { /* Do something with d which is now a variable of type Derived */ } } 

Lo cual sería equivalente a:

 void Funtion(Base b) { Defined d; if (b is Derived) { d = (Defined)b; /* Do something with d */ } } 

Ahora puedes llamar:

 Function(new Derived()); // Will execute code defined in if 

Tanto como

 Function(new Base()); // Won't execute code defined in if 

¡De esta forma puede estar seguro de que su downcast será válido y no arrojará una excepción!

Para lanzar, el objeto real debe ser de un Tipo igual o derivado del Tipo que está intentando lanzar …

o, para decirlo de la manera opuesta, el tipo al que intentas convertirlo debe ser el mismo o una clase base del tipo real del objeto.

si su objeto real es de tipo Baseclass , entonces no puede convertirlo a una clase derivada Tipo …

Una variación en el enfoque de serialización para aquellos que usan ServiceStack:

 var child = baseObject.ConvertTo(); 

o el más detallado:

 var child = baseObject.ToJson().FromJson(); 

La serialización de ServiceStack puede ser súper rápida y todo, pero claramente, esta no es una solución para conversiones masivas en transferencias de baja latencia, ni para tipos altamente complejos. Eso es obvio para cualquiera que use ServiceStack, pero pensé que lo aclararía antes de los comentarios.

En cuanto a mí, fue suficiente copiar todos los campos de propiedad de la clase base al padre como este:

 using System.Reflection; public static ChildClass Clone(BaseClass b) { ChildClass p = new ChildClass(...); // Getting properties of base class PropertyInfo[] properties = typeof(BaseClass).GetProperties(); // Copy all properties to parent class foreach (PropertyInfo pi in properties) { if (pi.CanWrite) pi.SetValue(p, pi.GetValue(b, null), null); } return p; } 

Una solución universal para cualquier objeto se puede encontrar aquí