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


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

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

Author: Bassie, 2009-04-08

19 answers

No. Una referencia a una clase derivada debe referirse realmente a una instancia de la clase derivada (o null). 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 derivado adecuada. O mira tu árbol de herencia de nuevo e intenta rediseñarlo para que no tengas que hacerlo en primer lugar.

 79
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-08 11:15:26

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 la clase derivada puede hacer", 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).

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

Algo así:

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 derivado completamente funcional con valores predeterminados para los miembros derivados. De esta manera también puede 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!
 39
Author: Michael Klement,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-08 11:43:04

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 puedes usarlo en tu código de esta manera:

var base = new Base();
base.Data = 1;
var derived = base.As<Derived>();
Console.Write(derived.Data); // Would output 1
 16
Author: Markus Knappen Johansson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-03 14:43:58

Como muchos otros han respondido, No.

Utilizo el siguiente código en esas desafortunadas ocasiones en las que necesito usar un tipo base como un 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. Props 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>()

Dado que utiliza reflexión, es "caro". Utilizar consecuentemente.

 8
Author: MEC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-17 06:30:07

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 clase base.

 5
Author: ybo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-08 11:14:39

Puede convertir una variable que se escribe como la clase base al tipo de una clase derivada; sin embargo, por necesidad esto hará una comprobación de 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, creando una nueva instancia del segundo tipo, pero debe escribir el código de conversión manualmente.

 3
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-08 11:17:56

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

El método que prefiero y es bastante limpio, es usar un Mapeador de objetos como AutoMapper.

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

 3
Author: Mahmoodvcs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-29 05:50:53

Ampliando la respuesta de @ybo - no es posible porque la instancia que tiene de la clase base no es realmente una instancia de la clase derivada. Solo sabe de los miembros de la clase base, y no sabe nada de 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 en realidad ya es una instancia de la clase base, ya que ya tiene esos miembros. Lo contrario no se puede decir.

 2
Author: Andy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-08 11:16:04

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 on y TurnOffAC establece ACState en off. Si intenta usar las funciones TurnOnAC y TurnOffAC en el bus, no tiene sentido.

 2
Author: Rohit Dodle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-17 18:19:41

En realidad HAY una manera 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 llenará todos los elementos que conoce.

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, serializar 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 como un encanto! Tan.. ¿cuándo es útil? Algunas personas preguntaron cuándo tendría sentido 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 clases (en. Net).

En mi caso, tengo una clase de configuración que contiene todos los ajustes "base" para un servicio. Los servicios específicos tienen más opciones y provienen de una tabla de BD diferente, por lo que esas clases heredan la clase base. Todos tienen un conjunto diferente de opciones. Por lo 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 BD. Justo después de eso, creo el objeto subclase utilizando el método descrito anteriormente. Luego hago una segunda consulta y lleno todos los valores dinámicos en el objeto subclase.

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

Este código de ejemplo es vb.Net, pero puede convertir 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)
 2
Author: Mark Sauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-07 12:44:15
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 clase base se inicia automáticamente para que la variable de referencia de clase base pueda apuntar al objeto de clase hijo.

Pero no viceversa porque una variable de referencia de clase hija no puede apuntar al objeto de clase base porque no se crea ningún objeto de clase hija.

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

 1
Author: shatrudhan kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-11 15:22:10

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 el Código:

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
Author: Ricardo Figueiredo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-30 00:06:51

Sé que esto es viejo, 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);
        }
    } 
 1
Author: Chris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-25 00:46:27

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 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 establecidas (no null) o no.

 0
Author: d.popov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-19 12:31:36

Podría no ser relevante, pero pude ejecutar código en un objeto derivado dada su base. Es definitivamente 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);
 0
Author: tstone2077,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-25 04:12:54

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

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

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 utiliza el operador (Program) deben definirse en ensamblajes separados (por ejemplo, el Derived la clase está en un library.dll que es referenciado por program.exe que contiene la clase Program).

//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 compila bien. Si solo hace referencia a library.dll, no hay garabatos.

 0
Author: Ark-kun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-28 20:37:43

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

Usted obtiene tres beneficios utilizando este enfoque.

  1. No estás duplicando el código
  2. No estás usando reflexión (que es lenta)
  3. Todas tus conversiones están en un solo lugar
 0
Author: adeel41,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-19 13:55:41

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í, podría hacerse más eficiente, no, no es lo mejor desde el pan rebanado, sí, hay mapeadores de objetos de paquete nuget robustos y completos que son mucho mejores para uso pesado, etc, etc, yada yada-pero funciona para nuestras necesidades básicas sin embargo:)

Y, por supuesto, intentará asignar valores de cualquier objeto a cualquier objeto, derivado o no (solo las propiedades públicas que se denominan igual, 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);
            }
        });
    }
}
 0
Author: AVH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-18 17:32:40

No, ver 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 método Initialise

 -1
Author: Aaron Powell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:19