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

StackOverflow https://stackoverflow.com/questions/729527

Pregunta

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

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

¿Fue útil?

Solución

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

Por ejemplo:

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

Si usted quiere ser capaz de convertir una instancia del tipo base al tipo derivado, Le sugiero que escriba un método para crear una instancia de tipo derivada adecuada. O mirar el árbol de herencia de nuevo y tratar de rediseñar de manera que no es necesario hacer esto en primer lugar.

Otros consejos

No, eso no es posible, ya que la asignación a una referencia de clase derivada sería como decir "Clase base es un sustituto completamente capaz de clase derivada, se puede hacer todo a la clase derivada puede hacer", lo cual no es cierto, ya que las clases derivadas en general, ofrecer más funcionalidad que la clase base (por lo menos, esa es la idea detrás de la herencia).

Se puede escribir un constructor de la clase derivada de tomar un objeto de la clase base como parámetro, la copia de 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, deberá copiar el objeto base y obtener un objeto de clase derivada completamente funcional con valores por defecto para los miembros de derivados. De esta manera también se 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!

I tenía este problema y lo resolvió mediante la adición de 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;
}

Esto significa que se puede usar en ti código como el siguiente:

var base = new Base();
base.Data = 1;
var derived = base.As<Derived>();
Console.Write(derived.Data); // Would output 1

Como muchos otros han contestado, n.

utilizo el siguiente código en esas desafortunadas ocasiones cuando necesito utilizar un tipo de base como un tipo derivado. Sí, es una violación del principio de sustitución de liskov (LSP) y sí la mayor parte del tiempo estamos a favor de la composición sobre la herencia. Apoyos a Markus Johansson Knappen cuya respuesta original esto se basa en.

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 se usa la reflexión, es "caro". Utilizar en consecuencia.

No, no es posible, por lo tanto, el error de ejecución.

Sin embargo, se puede asignar una instancia de una clase derivada de una variable del tipo de la clase base.

Como todo el mundo aquí, dijo, eso no es posible directamente.

El método que prefieren, y bastante limpio, es utilizar un asignador de objetos como AutoMapper .

Lo hará la tarea de propiedades copiar de un ejemplo a otro (no necesariamente del mismo tipo) de forma automática.

Puede emitir un Variable que se escribe como el de la clase base con el tipo de una clase derivada; Sin embargo, por necesidad, esto va a hacer una comprobación en tiempo de ejecución, para ver si el objeto real en cuestión es del tipo correcto.

Una vez creado, el type de un objeto no se puede cambiar (no menos importante, puede que no sea del mismo tamaño). Puede, sin embargo, convertir una instancia, la creación de un nueva ejemplo del segundo tipo -. Pero hay que escribir el código de conversión manualmente

Ampliando la respuesta de @ YBO - no es posible debido a que la instancia que tiene de la clase base no es realmente una instancia de la clase derivada. Sólo se sabe acerca de los miembros de la clase base, y no sabe nada de los de la clase derivada.

La razón por la que usted puede echar una instancia de la clase derivada a una instancia de la clase base se debe a que la clase derivada de hecho ya es una instancia de la clase base, ya que tiene los miembros ya. Lo contrario no se puede decir.

No, no es posible.

Considere un escenario donde un ACBus es una clase derivada de Bus clase base. ACBus tiene características como TurnOnAC y TurnOffAC que operan en un campo denominado ACState. TurnOnAC establece ACState de encendido y TurnOffAC establece ACState en off. Si intenta utilizar las funciones TurnOnAC y TurnOffAC en autobús, que no tiene sentido.

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 de niño, el objeto de la clase base es auto iniciado tan básica variable de referencia de clase puede señalar a objeto de clase de niño.

pero no viceversa, porque una variable de referencia de clase niño no puede apuntar a objeto de clase base porque no se crea ningún objeto de clase de niño.

y también note que la variable de referencia de la clase base sólo puede llamar a miembros de la clase base.

En realidad, hay una manera de hacer esto. Piense en cómo se puede utilizar Newtonsoft JSON para deserializar un objeto de JSON. Lo hará (o al menos pueden) hacer caso omiso de los elementos que faltan y llenar todos los elementos que no conocen.

Así que aquí es cómo lo hice. Un pequeño ejemplo de código va a seguir mi explicación.

  1. Crea una instancia del objeto de la clase base y rellenarla en consecuencia.

  2. Uso de la clase "jsonconvert" de Newtonsoft JSON, que serializar objeto en una cadena JSON.

  3. Ahora crear el objeto de subclase de deserializar con la cadena JSON creado en el paso 2. Esto creará una instancia de la subclase con todas las propiedades de la clase base.

Esto funciona como un encanto! Así que .. cuando es útil? Algunas personas preguntaron si esto tendría sentido y sugirieron cambiar el esquema de la 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 de "base" para un servicio. Los servicios específicos tienen más opciones y los que provienen de una tabla de base de datos diferente, por lo que las clases heredan de la clase base. Todos ellos tienen un conjunto de opciones diferentes. Así que cuando se recuperan los datos de un servicio, es mucho más fácil para poblar los valores PRIMERA utilizando una instancia del objeto base. Un método para hacer esto con una sola consulta de base de datos. Justo después de eso, se crea el objeto subclase utilizando el método descrito anteriormente. luego hago una segunda consulta y llenar todos los valores dinámicos en el objeto de subclase.

El resultado final es una clase derivada con todas las opciones establecidas. La repetición de este para las nuevas clases secundarias adicionales tarda sólo unas pocas líneas de código. Es muy sencillo, y utiliza un paquete muy probado y comprobado (Newtonsoft) para hacer el trabajo mágico.

Este código de ejemplo es vb.Net, pero se 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)

Puede que no sea relevante, pero yo era capaz de ejecutar código en un objeto derivado dada su base. Es definitivamente más hacky 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);

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

}

Sé que esto es viejo, pero he utilizado esto 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);
        }
    } 

Solución con JsonConvert (en lugar de encasillado)

Hoy me enfrenté al mismo problema y me encontré con un solución rápida y sencilla para el problema utilizando JsonConvert.

var base = new BaseClass();
var json = JsonConvert.SerializeObject(base);
DerivedClass derived = JsonConvert.DeserializeObject<DerivedClass>(json);

Otra solución es añadir método de extensión de este modo:

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

entonces tener 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 propiedades de destino si ya establecido (no nulo) o no.

  

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

No sólo explícita, sino también las conversiones implícitas son posibles.

lenguaje C # no permite este tipo de operadores de conversión, pero todavía se puede escribir usando C # puro y trabajar. Tenga en cuenta que la clase que define el operador implícito de conversión (Derived) y la clase que utiliza el operador (Program) debe definirse en conjuntos separados (por ejemplo, la clase Derived está en una library.dll que se hace referencia 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 utilizando la referencia del proyecto en Visual Studio, VS muestra garabatos cuando se utiliza la conversión implícita, pero compila bien. Si usted acaba de hacer referencia al library.dll, no hay garabatos.

Puede hacer esto utilizando 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 consigue tres beneficios que utilizan este enfoque.

  1. No está duplicando el código
  2. No está utilizando la reflexión (que es lento)
  3. Todas las conversiones se encuentran en un solo lugar

he combinado algunas partes de las respuestas anteriores (gracias a los autores) y poner juntos una clase estática simple con dos métodos que estamos utilizando.

Sí, es muy sencillo, sin que no cubre todos los escenarios, sí que podría ser ampliado y hecho mejor, no, no es perfecto, sí que podría posiblemente ser más eficiente, No, no es lo más grande desde el pan de molde, sí hay completo en la robusta objeto de paquete Nuget correlacionadores por ahí que son mucho mejor para el uso pesado, etc, etc, bla, bla - pero funciona para nuestras necesidades básicas aunque :)

Y, por supuesto, se tratará de asignar los valores de cualquier objeto a cualquier objeto, que se deriven o no (sólo las propiedades públicas que llevan el nombre de la misma, 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);

STATIC clase de utilidad:

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

¿Qué hay de:

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

La mejor manera de añadir todas las propiedades de base de elemento derivado es el uso reflejo en costructor. Prueba 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) { }
        }

    }

No estoy de acuerdo que no es posible. Puede hacerlo de esta manera:

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

No, mira a esta pregunta que pedí - upcasting en .NET utilizando los genéricos

La mejor manera es hacer un constructor predeterminado de la clase, construir y luego llamar a un método Initialise

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top