c# - clases - ¿Es posible asignar un objeto de clase base a una referencia de clase derivada con un typecast explícito?
clases de derivadas calculo (17)
¿Es posible asignar un objeto de clase base a una referencia de clase derivada con un tipo de transmisión explícita en C # ?.
No solo son posibles las conversiones explícitas, sino también implícitas.
El lenguaje C # no permite dichos 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ícita ( Derived
) y la clase que utiliza el operador ( Program
) se deben definir en ensamblajes separados (por ejemplo, la clase Derived
está en una library.dll
que hace referencia program.exe
contiene el Program
clase).
//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 utilizando la referencia de proyecto en Visual Studio, VS muestra garabatos cuando utiliza la conversión implícita, pero se compila muy bien. Si solo hace referencia a library.dll
, no hay garabatos.
¿Es posible asignar un objeto de clase base a una referencia de clase derivada con un tipo de transmisión explícita en C # ?.
Lo he intentado y crea un error en tiempo de ejecución.
Ampliar 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 sabe sobre 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 en una instancia de la clase base es porque la clase derivada en realidad ya es una instancia de la clase base, ya que ya tiene esos miembros. Lo opuesto no puede ser dicho.
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 preferimos la composición a la herencia. Apoyos a 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>()
Como usa reflexión, es "caro". Use en consecuencia.
Como todos dijeron 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.
En realidad, hay una manera de hacer esto. Piensa en cómo puedes usar Newtonsoft JSON para deserializar un objeto de json. Ignorará (o al menos podrá) los elementos faltantes y puebla todos los elementos que sí conoce.
Entonces, así es como lo hice. Una pequeña muestra de código seguirá mi explicación.
Cree una instancia de su objeto a partir de la clase base y rellene en consecuencia.
Utilizando la clase "jsonconvert" de Newtonsoft json, serialice ese objeto en una cadena json.
Ahora crea tu objeto de clase secundaria deserializando con la cadena json creada en el paso 2. Esto creará una instancia de tu subclase con todas las propiedades de la clase base.
Esto funciona como un encanto! Entonces ... ¿Cuándo es esto útil? Algunas personas preguntaron cuándo tendría sentido esto y sugirieron cambiar el esquema del OP para acomodar el hecho de que no se puede hacer esto de forma nativa con la herencia de clase (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 DB diferente, por lo que esas clases heredan la clase base. Todos ellos tienen un conjunto diferente de opciones. Por lo tanto, al recuperar los datos para un servicio, es mucho más fácil PRIMERO llenar los valores usando una instancia del objeto base. Un método para hacer esto con una sola consulta DB. Inmediatamente después, creo el objeto de la clase secundaria utilizando el método descrito anteriormente. Luego realizo una segunda consulta y llene todos los valores dinámicos en el objeto de la clase secundaria.
El resultado final es una clase derivada con todas las opciones establecidas. Repitiendo esto para nuevas subclases adicionales toma 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 en 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)
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 para la clase derivada, puede hacer todo lo que la clase derivada puede hacer", lo que 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).
Puede 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, debe copiar el objeto base y obtener un objeto de clase derivado totalmente funcional con valores predeterminados para los miembros derivados. De esta forma también puedes evitar el problema señalado por Jon Skeet:
Base b = new Base();
Dervided d = new Derived();
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!
No, mira esta pregunta que hice: Upcasting en .NET usando genéricos
La mejor forma es crear un constructor predeterminado en la clase, construir y luego llamar a un método de Initialise
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 del tipo de clase base.
No, no es posible.
Considere un escenario donde un ACBus es una clase derivada de Bus de clase base. ACBus tiene características como TurnOnAC y TurnOffAC que operan en un campo llamado ACState. TurnOnAC establece ACState en activado y TurnOffAC desactiva ACState. Si intenta utilizar las funciones TurnOnAC y TurnOffAC en Bus, no tiene sentido.
No. Una referencia a una clase derivada debe referirse a una instancia de la clase derivada (o nulo). De lo contrario, ¿cómo esperarías que se comportara?
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 al tipo derivado, le sugiero que escriba un método para crear una instancia de tipo derivada apropiada. O revise nuevamente su árbol de herencia e intente rediseñar para no tener que hacer esto en primer lugar.
Otra solución es agregar el 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 tiene un constructor en cada clase derivada que acepta clase base:
public class DerivedClass: BaseClass
{
public DerivedClass(BaseClass baseModel)
{
this.CopyProperties(baseModel);
}
}
También sobrescribirá opcionalmente las propiedades de destino si ya está configurado (no nulo) o no.
Puede convertir una variable que se escribe como la clase base al tipo de una clase derivada; sin embargo, por necesidad, esto hará un control en tiempo de ejecución, para ver si el objeto real involucrado es del tipo correcto.
Una vez creado, el tipo de un objeto no se puede cambiar (no menos importante, puede que no sea del mismo tamaño). Sin embargo, puede convertir una instancia, crear una nueva instancia del segundo tipo, pero debe escribir el código de conversión manualmente.
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);
Puedes hacer esto usando genérico.
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; }
}
Obtienes tres beneficios usando este enfoque.
- No estás duplicando el código
- No estás usando el reflejo (que es lento)
- Todas sus conversiones están en un solo lugar
Puedes usar 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);
}
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
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 hijo, el objeto de la clase base se inicia automáticamente, por lo que la variable de referencia de la clase base puede apuntar a un objeto de clase hijo.
pero no al revés porque una variable de referencia de clase hija no puede apuntar a un objeto de clase base porque no se crea ningún objeto de clase hijo.
y también observe que la variable de referencia de la clase base solo puede llamar al miembro de la clase base.