No se puede transmitir de la clase principal a la clase secundaria
97
Estoy tratando de transmitir de una clase principal a una clase secundaria pero obtengo una InvalidCastException. La clase secundaria solo tiene una propiedad de tipo int. ¿Alguien sabe lo que tengo que hacer?
Solo una nota, los nombres de las variables no son los mismos arriba.
Jake Gaston
5
¡Me encanta cuando alguien piensa fuera de la caja y silencia a la gente que le dice al OP que no se puede hacer (excepto por uno o dos trolls)! Gracias por la ayuda en este. He estado tratando de resolver esto durante las últimas horas :)
derekmx271
3
Ésta es una excelente solución. Tuve un caso en el que mi clase secundaria era solo un contenedor para un padre sin funcionalidad adicional. Hice eso para no tener que importar la referencia web a mi aplicación, ya que estaba en mi biblioteca auxiliar. Esto me permitió convertir el padre en mi clase contenedora. ¡Gracias!
BrianVPS
1
¡Eres un genio! :)
Yablargo
118
No puedes convertir un mamífero en un perro, podría ser un gato.
No se puede meter una comida en un sándwich, podría ser una hamburguesa con queso.
No se puede convertir un automóvil en un Ferrari; puede ser un Honda, o más específicamente, no se puede convertir un Ferrari 360 Modena en un Ferrari 360 Challange Stradale; hay partes diferentes, aunque ambos son Ferrari 360.
Obstáculos comprensibles, de ahí la imposibilidad de 'lanzar' realmente de esta manera. Pero, ¿qué pasa si quiere un perro que tenga el mismo color de ojos / peso / patrón de pelo / edad, etc. que el gato que está siendo retenido en el objeto mamífero? Esencialmente copiando las propiedades comunes.
FastAl
7
FastAl, esta es exactamente la razón por la que tenemos interfaces. Mammal debe implementar IMammal y contener color de ojos, peso, etc. Ahora puede lanzar tanto perro como gato a IMammal.
Tom Deloford
1
Puedes convertir un mamífero en un perro. Si es un perro, es un perro. De lo contrario, se vuelve nulo. Las funciones de "sobrecarga" pueden hacer posible la conversión imposible de gato a perro, si el gato tiene estas funciones sobrecargadas que lo permitan. Pero es su trabajo manejar la pérdida de datos y adaptar los datos que no existen. Como convertir garras en clavos, perseguir cuerdas en perseguir bolas, etc ...
TamusJRoyce
Creo que los ejemplos son un poco extremos y selectivos y quizás el elenco sea un atajo para un constructor de copias. Por ejemplo, estar construyendo un ferrari con las propiedades definidas en el objeto base car. O comience con un humano y cree un niño. ¿Casting y uso directo? De acuerdo, eso es un no-no. Pero si es parte del constructor o algo así, podría funcionar. La respuesta de serialización a continuación es un buen toque.
sirthomas
1
Analogía de Ferrari NICE
Lord Darth Vader
57
La instancia a la que se refiere la referencia de su clase base no es una instancia de su clase secundaria. No hay nada malo.
Más específicamente:
Base derivedInstance =newDerived();Base baseInstance =newBase();Derived good =(Derived)derivedInstance;// OKDerived fail =(Derived)baseInstance;// Throws InvalidCastException
Para que el reparto sea exitoso, la instancia a la que estás rebajando debe ser una instancia de la clase a la que estás rebajando (o al menos, la clase a la que estás rebajando debe estar dentro de la jerarquía de clases de la instancia); de lo contrario, el el elenco fallará.
o potencialmente Base otherDerived = new OtherDerived (); Derived otherFail = (Derived) otherDerived;
Blair Conrad
class Base {} class Derived: Base {} // En el método principal Base derivadaInstance = new Derived (); Base baseInstance = new Base (); Bien derivado = (derivado) derivadoInstance; Fallo derivado = (Derivado) baseInstance; Esto se compila sin ningún error en .NET 3.5. ¿Dónde está el problema que estás diciendo?
pradeeptp
7
@pradeeptp: Por supuesto que construye. ¿Quién dijo algo sobre un error de compilación?
Greg D
17
Hay algunos casos en los que tal reparto tendría sentido.
En mi caso, estaba recibiendo una clase BASE a través de la red y necesitaba más funciones. Así que derivarlo para manejarlo de mi lado con todas las campanas y silbidos que quería, y convertir la clase BASE recibida en la DERIVED simplemente no era una opción (lanza InvalidCastException of Course)
Una SOLUCIÓN práctica de pensar fuera de la caja fue declarar una clase EXTENSION Helper que NO estaba heredando la clase BASE en realidad, sino que la INCLUYE como miembro.
publicclassBaseExtension{Base baseInstance;publicFakeDerived(Base b){
baseInstance = b;}//Helper methods and extensions to Base class added here}
Si tiene un acoplamiento suelto y solo necesita un par de características adicionales para la clase base sin REALMENTE tener una necesidad absoluta de derivación, esa podría ser una solución rápida y sencilla.
¿Estoy en lo cierto al pensar que probablemente desee que su BaseExtensionaquí al menos implemente de IBasemanera que pueda usarlo en contextos similares? ¿O no era importante para tus necesidades?
tobriand
algunas veces, incluir puede ser una sustitución apropiada para la herencia
Vahid Ghadiri
17
He visto a la mayoría de la gente decir que el casting explícito de padre a hijo no es posible, eso en realidad no es cierto. Tomemos un comienzo revisado e intentemos probarlo con ejemplos.
Como sabemos en .net todos los castings tienen dos amplias categorías.
Para el tipo de valor
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 hijo a padre (conversión implícita: siempre con éxito)
Caso 1. Hijo de cualquier padre directo o indirecto
Employee e =newEmployee();Person p =(Person)e;//Allowed
Padre a hijo (transmisión explícita: puede tener éxito)
Caso 2. Variable principal que contiene el objeto principal (no permitido)
Person p =newPerson();// p is true Person objectEmployee e =(Employee)p;//Runtime err : InvalidCastException <-------- Yours issue
Caso 3. Variable principal que contiene el objeto secundario (siempre correcto)
Nota: Debido a que los objetos tienen naturaleza polimórfica, es posible que una variable de un tipo de clase principal contenga un tipo secundario.
Person p =newEmployee();// p actually is EmployeeEmployee e =(Employee)p;// Casting allowed
Conclusión: Después de leer sobre todo, espero que ahora tenga sentido como es posible la conversión de padres a hijos (Caso 3).
Respuesta a la pregunta :
Tu respuesta es en el caso 2, donde puedes ver que tal conversión no está permitida por OOP y estás tratando de violar una de las reglas básicas de OOP, así que elige siempre la ruta segura.
Además, para evitar situaciones excepcionales, .net ha recomendado el uso de operadores is / as que le ayudarán a tomar decisiones informadas y proporcionar un casting seguro.
Eso violaría los principios orientados a objetos. Yo diría que una solución elegante aquí y en otras partes del proyecto es usar un marco de mapeo de objetos como AutoMapper para configurar una proyección.
Aquí hay una configuración un poco más compleja de la necesaria, pero lo suficientemente flexible para la mayoría de los casos:
Las propiedades se asignan por convención, por lo que si la clase se hereda, los nombres de las propiedades son exactamente iguales y la asignación se configura automáticamente. Puede agregar propiedades adicionales ajustando la configuración. Consulte la documentación .
Usar Automapper para mapear un tipo con una sola propiedad a otro (como el OP descrito) es como usar un martillo para romper un huevo. ¿Por qué no simplemente crear el tipo derivado y asignar su propiedad usted mismo (que es 1 línea de código)?
bytedev
9
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 manera genérica solo una vez, luego inicializamos propiedades específicas para las clases derivadas. Utilizo VB, por lo que mi muestra está en VB (noogies difíciles), pero robé la muestra VB de este sitio que también tiene una mejor versión de C #:
ImportsSystemImportsSystem.Collections.GenericImportsSystem.ReflectionImportsSystem.TextImportsSystem.DiagnosticsModuleClassUtilsPublicSubCopyProperties(ByVal dst AsObject,ByVal src AsObject)Dim srcProperties()AsPropertyInfo= src.GetType.GetPropertiesDim dstType = dst.GetTypeIf srcProperties IsNothingOr dstType.GetPropertiesIsNothingThenReturnEndIfForEach srcProperty AsPropertyInfoIn srcProperties
Dim dstProperty AsPropertyInfo= dstType.GetProperty(srcProperty.Name)If dstProperty IsNotNothingThenIf dstProperty.PropertyType.IsAssignableFrom(srcProperty.PropertyType)=TrueThen
dstProperty.SetValue(dst, srcProperty.GetValue(src,Nothing),Nothing)EndIfEndIfNextEndSubEndModuleModuleModule1Class base_class
Dim _bval AsIntegerPublicProperty bval()AsIntegerGetReturn _bval
EndGetSet(ByValvalueAsInteger)
_bval =valueEndSetEndPropertyEndClassClass derived_class
Inherits base_class
Public _dval AsIntegerPublicProperty dval()AsIntegerGetReturn _dval
EndGetSet(ByValvalueAsInteger)
_dval =valueEndSetEndPropertyEndClassSubMain()' NARROWING CONVERSION TEST
Dim b AsNew base_class
b.bval =10Dim 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 =20Console.WriteLine(b.bval)Console.WriteLine(d.bval)Console.WriteLine(d.dval)Console.ReadLine()EndSubEndModule
Por supuesto, esto no es realmente un casting. Está creando un nuevo objeto derivado y copiando las propiedades del padre, dejando las propiedades del hijo en blanco. Eso es todo lo que necesitaba hacer y parece que es todo lo que necesitas hacer. Tenga en cuenta que solo copia propiedades, no miembros (variables públicas) en la clase (pero puede extenderlo para hacer eso si es una vergüenza exponer miembros públicos).
La transmisión en general crea 2 variables que apuntan al mismo objeto (mini tutorial aquí, por favor no me arrojes excepciones de casos de esquina). ¡Esto tiene ramificaciones significativas (ejercicio para el lector)!
Por supuesto, tengo que decir por qué el lenguaje no te deja ir de la base a la instancia derivada, sino al revés. Imagine un caso en el que puede tomar una instancia de un cuadro de texto winforms (derivado) y almacenarlo en una variable de tipo control Winforms. Por supuesto, el 'control' puede mover el objeto alrededor de OK y usted puede lidiar con todas las cosas de 'control' sobre el cuadro de texto (por ejemplo, arriba, izquierda, propiedades de texto). Las cosas específicas del cuadro de texto (por ejemplo, .multiline) no se pueden ver sin lanzar la variable de tipo 'control' apuntando al cuadro de texto en la memoria, pero todavía está ahí en la memoria.
Ahora imagina que tienes un control y quieres ponerle una variable de tipo cuadro de texto. Al Control en la memoria le falta 'multilínea' y otras cosas de caja de texto. Si intenta hacer referencia a ellos, ¡el control no hará crecer mágicamente una propiedad de varias líneas! La propiedad (mírala como una variable miembro aquí, que realmente almacena un valor, porque está activado en la memoria de la instancia del cuadro de texto) debe existir. Ya que está lanzando, recuerde, tiene que ser el mismo objeto al que está apuntando. Por tanto, no se trata de una restricción de lenguaje, es filosóficamente imposible de aplicar casos de esa manera.
Sé que esto es mucho después del hecho, pero debe incluir "AndAlso dstProperty.CanWrite" en su prueba "If dstProperty IsNot Nothing", para asegurarse de que no sea una propiedad de solo lectura.
JamesMLV
@JamesMLV - gracias buena captura. 'después del hecho' - no parece que el OP vaya a aceptar ninguna respuesta de todos modos :-( por lo que no hay ningún hecho a
seguir
4
La instancia del objeto debe crearse utilizando el tipo de la clase secundaria, no puede convertir una instancia de tipo principal a un tipo secundario
En cuanto a mí, fue suficiente copiar todos los campos de propiedad de la clase base al padre así:
using System.Reflection;publicstaticChildClassClone(BaseClass b){ChildClass p =newChildClass(...);// Getting properties of base classPropertyInfo[] properties =typeof(BaseClass).GetProperties();// Copy all properties to parent classforeach(PropertyInfo pi in properties){if(pi.CanWrite)
pi.SetValue(p, pi.GetValue(b,null),null);}return p;}
Aquí se puede encontrar una solución universal para cualquier objeto.
Una variación del enfoque de serialización para quienes usan ServiceStack:
var child = baseObject.ConvertTo<ChildType>();
o el más detallado:
var child = baseObject.ToJson().FromJson<ChildType>();
La serialización de ServiceStack puede ser súper rápida y todo eso, pero claramente, esta no es una solución para conversiones masivas en transferencias de baja latencia, ni para tipos muy complejos. Es probable que sea obvio para cualquiera que use ServiceStack, pero pensé en aclararlo antes de los comentarios.
Respuestas:
Una forma sencilla de abatir en C # es serializar el padre y luego deserializarlo en el hijo.
Tengo una aplicación de consola simple que convierte un animal en un perro, usando las dos líneas de código anteriores aquí
fuente
No puedes convertir un mamífero en un perro, podría ser un gato.
No se puede meter una comida en un sándwich, podría ser una hamburguesa con queso.
No se puede convertir un automóvil en un Ferrari; puede ser un Honda, o más específicamente, no se puede convertir un Ferrari 360 Modena en un Ferrari 360 Challange Stradale; hay partes diferentes, aunque ambos son Ferrari 360.
fuente
La instancia a la que se refiere la referencia de su clase base no es una instancia de su clase secundaria. No hay nada malo.
Más específicamente:
Para que el reparto sea exitoso, la instancia a la que estás rebajando debe ser una instancia de la clase a la que estás rebajando (o al menos, la clase a la que estás rebajando debe estar dentro de la jerarquía de clases de la instancia); de lo contrario, el el elenco fallará.
fuente
Hay algunos casos en los que tal reparto tendría sentido.
En mi caso, estaba recibiendo una clase BASE a través de la red y necesitaba más funciones. Así que derivarlo para manejarlo de mi lado con todas las campanas y silbidos que quería, y convertir la clase BASE recibida en la DERIVED simplemente no era una opción (lanza InvalidCastException of Course)
Una SOLUCIÓN práctica de pensar fuera de la caja fue declarar una clase EXTENSION Helper que NO estaba heredando la clase BASE en realidad, sino que la INCLUYE como miembro.
Si tiene un acoplamiento suelto y solo necesita un par de características adicionales para la clase base sin REALMENTE tener una necesidad absoluta de derivación, esa podría ser una solución rápida y sencilla.
fuente
BaseExtension
aquí al menos implemente deIBase
manera que pueda usarlo en contextos similares? ¿O no era importante para tus necesidades?He visto a la mayoría de la gente decir que el casting explícito de padre a hijo no es posible, eso en realidad no es cierto. Tomemos un comienzo revisado e intentemos probarlo con ejemplos.
Como sabemos en .net todos los castings tienen dos amplias categorías.
El tipo de referencia tiene otros tres casos situacionales principales en los que puede encontrarse cualquier escenario.
De hijo a padre (conversión implícita: siempre con éxito)
Padre a hijo (transmisión explícita: puede tener éxito)
Conclusión: Después de leer sobre todo, espero que ahora tenga sentido como es posible la conversión de padres a hijos (Caso 3).
fuente
Eso violaría los principios orientados a objetos. Yo diría que una solución elegante aquí y en otras partes del proyecto es usar un marco de mapeo de objetos como AutoMapper para configurar una proyección.
Aquí hay una configuración un poco más compleja de la necesaria, pero lo suficientemente flexible para la mayoría de los casos:
Cuando la aplicación comienza a llamar
AutoMapperConfiguration.Configure()
y luego puede proyectar así:Las propiedades se asignan por convención, por lo que si la clase se hereda, los nombres de las propiedades son exactamente iguales y la asignación se configura automáticamente. Puede agregar propiedades adicionales ajustando la configuración. Consulte la documentación .
fuente
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 manera genérica solo una vez, luego inicializamos propiedades específicas para las clases derivadas. Utilizo VB, por lo que mi muestra está en VB (noogies difíciles), pero robé la muestra 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:
Por supuesto, esto no es realmente un casting. Está creando un nuevo objeto derivado y copiando las propiedades del padre, dejando las propiedades del hijo en blanco. Eso es todo lo que necesitaba hacer y parece que es todo lo que necesitas hacer. Tenga en cuenta que solo copia propiedades, no miembros (variables públicas) en la clase (pero puede extenderlo para hacer eso si es una vergüenza exponer miembros públicos).
La transmisión en general crea 2 variables que apuntan al mismo objeto (mini tutorial aquí, por favor no me arrojes excepciones de casos de esquina). ¡Esto tiene ramificaciones significativas (ejercicio para el lector)!
Por supuesto, tengo que decir por qué el lenguaje no te deja ir de la base a la instancia derivada, sino al revés. Imagine un caso en el que puede tomar una instancia de un cuadro de texto winforms (derivado) y almacenarlo en una variable de tipo control Winforms. Por supuesto, el 'control' puede mover el objeto alrededor de OK y usted puede lidiar con todas las cosas de 'control' sobre el cuadro de texto (por ejemplo, arriba, izquierda, propiedades de texto). Las cosas específicas del cuadro de texto (por ejemplo, .multiline) no se pueden ver sin lanzar la variable de tipo 'control' apuntando al cuadro de texto en la memoria, pero todavía está ahí en la memoria.
Ahora imagina que tienes un control y quieres ponerle una variable de tipo cuadro de texto. Al Control en la memoria le falta 'multilínea' y otras cosas de caja de texto. Si intenta hacer referencia a ellos, ¡el control no hará crecer mágicamente una propiedad de varias líneas! La propiedad (mírala como una variable miembro aquí, que realmente almacena un valor, porque está activado en la memoria de la instancia del cuadro de texto) debe existir. Ya que está lanzando, recuerde, tiene que ser el mismo objeto al que está apuntando. Por tanto, no se trata de una restricción de lenguaje, es filosóficamente imposible de aplicar casos de esa manera.
fuente
La instancia del objeto debe crearse utilizando el tipo de la clase secundaria, no puede convertir una instancia de tipo principal a un tipo secundario
fuente
En cuanto a mí, fue suficiente copiar todos los campos de propiedad de la clase base al padre así:
Aquí se puede encontrar una solución universal para cualquier objeto.
fuente
A partir de C # 7.0, puede usar la palabra clave is para hacer esto:
Con esas clases definidas:
Luego puedes hacer algo como:
Lo que equivaldría a:
Ahora puedes llamar:
Tanto como
De esa manera, puede estar seguro de que su abatimiento será válido y no lanzará una excepción.
fuente
Para lanzar, el objeto real debe ser de un Tipo igual o derivado del Tipo al que estás intentando lanzar ...
o, para decirlo de la manera opuesta, el tipo al que está intentando 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 en una clase derivada Type ...
fuente
Una variación del enfoque de serialización para quienes usan ServiceStack:
o el más detallado:
La serialización de ServiceStack puede ser súper rápida y todo eso, pero claramente, esta no es una solución para conversiones masivas en transferencias de baja latencia, ni para tipos muy complejos. Es probable que sea obvio para cualquiera que use ServiceStack, pero pensé en aclararlo antes de los comentarios.
fuente