Est-il possible d'assigner un objet de classe de base à une référence de classe dérivée avec une conversion de type explicite?

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

Question

Est-il possible d'assigner un objet de classe de base à une référence de classe dérivée avec une conversion de type explicite dans C # ?.

Je l'ai essayé et il crée une erreur d'exécution.

Était-ce utile?

La solution

Non. Une référence à une classe dérivée doit effectivement se référer à une instance de la classe dérivée (ou nul). Sinon, comment voulez-vous attendre à se comporter?

Par exemple:

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

Si vous voulez être en mesure de convertir une instance du type de base du type dérivé, je vous suggère d'écrire une méthode pour créer une instance de type dérivé de approprié. Ou regardez votre arbre d'héritage et d'essayer de remanier de sorte que vous n'avez pas besoin de le faire en premier lieu.

Autres conseils

Non, ce n'est pas possible depuis l'attribuer à une référence de classe dérivée serait comme dire « classe de base est un substitut tout à fait capable de classe dérivée, il peut faire tout ce que la classe dérivée peut faire », ce qui est vrai que les classes dérivées en général offrent plus de fonctionnalités que leur classe de base (au moins, c'est l'idée derrière l'héritage).

Vous pouvez écrire un constructeur dans la classe dérivée en prenant un objet de classe de base en tant que paramètre, la copie des valeurs.

Quelque chose comme ceci:

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

Dans ce cas, vous copiez l'objet de base et obtenir un objet de classe dérivée entièrement fonctionnelle avec des valeurs par défaut pour les membres dérivés. De cette façon, vous pouvez également éviter le problème signalé par 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 eu ce problème et résolu en ajoutant une méthode qui prend un paramètre de type et convertit l'objet courant dans ce type.

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

Cela signifie que vous pouvez l'utiliser dans votre code comme ceci:

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

Comme beaucoup d'autres ont répondu, non.

J'utilise le code suivant à ces occasions malheureux quand je dois utiliser un type de base comme un type dérivé. Oui, il est une violation du principe de substitution Liskov (LSP) et oui la plupart du temps, nous privilégions la composition de l'héritage. Props à Markus Knappen Johansson dont la réponse originale est basée sur ce.

Ce code dans la classe de 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;
    }

Permet:

    derivedObject = baseObect.As<derivedType>()

Comme il utilise la réflexion, il est « coûteux ». Utilisez en conséquence.

Non, il est impossible, par conséquent votre erreur d'exécution.

Mais vous pouvez assigner une instance d'une classe dérivée à une variable de type classe de base.

Comme tout le monde dit ici, ce n'est pas possible directement.

La méthode que je préfère et est plutôt propre, est d'utiliser un mappeur d'objets comme AutoMapper .

Il fera la tâche de copie des propriétés d'une instance à l'autre (pas nécessairement le même type) automatiquement.

Vous pouvez jeter un variable qui est typée comme la classe de base du type d'une classe dérivée; Cependant, par la nécessité que cela va faire un contrôle d'exécution, pour voir si l'objet réel impliqué est du type correct.

Une fois créé, le type d'un objet ne peut pas être changé (pas moins, il pourrait ne pas être la même taille). Vous pouvez, cependant, convertir une instance, la création d'un nouvelle instance du deuxième type -. Mais vous devez écrire le code de conversion manuellement

Développant la réponse de @ YBO - il est impossible car l'instance que vous avez de la classe de base n'est pas réellement une instance de la classe dérivée. Il ne connaît que les membres de la classe de base, et ne sait rien de ceux de la classe dérivée.

La raison pour laquelle vous pouvez lancer une instance de la classe dérivée à une instance de la classe de base est que la classe dérivée est en fait déjà une instance de la classe de base, car il a déjà les membres. Le contraire ne peut pas dire.

Non, il est impossible.

Considérons un scénario où un ACBus est une classe dérivée de la classe de base Bus. ACBus a des caractéristiques comme TurnOnAC et TurnOffAC qui fonctionnent sur un champ nommé ACState. TurnOnAC met à ACState sur et TurnOffAC met à ACState au large. Si vous essayez d'utiliser les fonctions TurnOnAC et TurnOffAC sur le bus, il n'a pas de sens.

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

}

lorsque nous créons un objet de classe des enfants, l'objet de classe de base est variable de référence automatique initié par classe afin de base peut pointer vers l'objet de classe enfant.

mais pas vice-versa, car une variable de référence de classe enfant ne peut pas pointer vers l'objet de classe de base parce qu'aucun objet de classe des enfants est créé.

et notez également que la variable de référence de classe de base ne peut appeler membre de la classe de base.

Il est en fait une façon de le faire. Pensez à la façon dont vous pouvez utiliser Newtonsoft JSON pour désérialiser un objet JSON. Il sera (ou tout au moins peut) ignorer les éléments manquants et remplir tous les éléments qu'il ne sait.

est donc ici que je l'ai fait. Un petit exemple de code suivra mon explication.

  1. Créer une instance de votre objet à partir de la classe de base et le remplir en conséquence.

  2. Utilisation de la classe "jsonconvert" de Newtonsoft JSON, sérialiser cet objet dans une chaîne JSON.

  3. Maintenant, créez votre sous objet de classe en désérialisant avec la chaîne JSON créé à l'étape 2. Cela va créer une instance de votre sous classe avec toutes les propriétés de la classe de base.

Cela fonctionne comme un charme! Alors .. quand cela est utile? Certaines personnes ont demandé quand ce serait logique et ont suggéré de modifier le schéma de l'OP pour tenir compte du fait que vous ne pouvez pas faire cela avec nativement l'héritage de classe (en .Net).

Dans mon cas, j'ai une classe de paramètres qui contient tous les paramètres « de base » pour un service. D'autres services ont plus d'options et celles qui proviennent d'une autre table de DB, de sorte que ces classes héritent de la classe de base. Ils ont tous un ensemble d'options différentes. Ainsi, lors de la récupération des données pour un service, il est beaucoup plus facile de remplir d'abord les valeurs à l'aide d'une instance de l'objet de base. Une méthode pour le faire avec une seule requête DB. Après cela, je crée l'objet de sous-classe en utilisant la méthode décrite ci-dessus. Je fais alors une deuxième requête et remplir toutes les valeurs dynamiques sur l'objet de sous-classe.

La sortie finale est une classe dérivée avec toutes les options définies. En répétant ce pour les classes supplémentaires nouvelles sous ne prend que quelques lignes de code. Il est simple, et il utilise un package très essayé et testé (Newtonsoft) pour faire le travail magique.

Ce code exemple est vb.Net, mais vous pouvez facilement convertir 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)

peut-être pas Relevent, mais j'ai pu exécuter du code sur un objet dérivé compte tenu de sa base. Il est certainement plus hacky que je le voudrais, mais cela fonctionne:

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

Vous pouvez utiliser une extension:

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

Dans le code:

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

}

Je sais que c'est vieux, mais je l'ai utilisé avec succès ce pour un bon moment.

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

Solution avec JsonConvert (au lieu de transtypage)

Aujourd'hui, je faisais face à la même question et je l'ai trouvé une solution simple et rapide au problème en utilisant JsonConvert.

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

Une autre solution consiste à ajouter méthode d'extension comme ceci:

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

ont alors un constructeur dans chaque classe dérivée qui accepte la classe de base:

  public class DerivedClass: BaseClass
    { 
        public DerivedClass(BaseClass baseModel)
        {
            this.CopyProperties(baseModel);
        }
    }

Il sera également le cas échéant remplacer les propriétés de destination si elle est déjà réglée (non nul) ou non.

  

Est-il possible d'assigner un objet de classe de base à une référence de classe dérivée avec une conversion de type explicite dans C # ?.

Non seulement explicite, mais aussi les conversions implicites sont possibles.

langage C # ne permet pas de tels opérateurs de conversion, mais vous pouvez toujours les écrire en utilisant C # pur et ils travaillent. Notez que la classe qui définit l'opérateur de conversion implicite (de Derived) et la classe qui utilise l'opérateur (Program) doit être défini dans les ensembles distincts (par exemple, la classe Derived se trouve dans une library.dll qui est référencé par program.exe contenant la classe 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.
    }
}

Lorsque vous faites référence à la bibliothèque en utilisant la référence de projet dans Visual Studio, VS montre gribouillis lorsque vous utilisez la conversion implicite, mais il compile très bien. Si vous faites référence à la juste library.dll, il n'y a pas gribouillis.

Vous pouvez le faire en utilisant générique.

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

Vous obtenez trois avantages en utilisant cette approche.

  1. Vous n'êtes pas dupliquer le code
  2. Vous n'utilisez pas la réflexion (qui est lent)
  3. Toutes vos conversions sont en un seul endroit

I combiné certaines parties des réponses précédentes (grâce à ces auteurs) et mis en place une simple classe statique avec deux méthodes que nous utilisons.

Oui, il est simple, non elle ne couvre pas tous les scénarios, oui, il pourrait être étendu et mieux, non ce n'est pas parfait, oui, il pourrait être plus efficace, non, ce n'est pas la plus grande chose depuis le pain tranché, oui, il y a plein sur l'objet de package NuGet robuste cartographes là-bas qui sont beaucoup mieux pour une utilisation intensive, etc etc, patata - mais il fonctionne pour nos besoins de base si :)

Et bien sûr, il va essayer de mapper les valeurs de tout objet à un objet, dérivé ou non (seulement les propriétés publiques qui porte le même nom bien sûr - ne tient pas compte du reste)

.

UTILISATION:

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 CLASSE UTILITAIRE:

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

Que diriez-vous:

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

La meilleure façon d'ajouter toutes les propriétés de base à l'élément dérivé est la réflexion d'utilisation dans costructor. Essayez ce code, sans créer des méthodes ou des instances.

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

    }

Je suis en désaccord qu'il est impossible. Vous pouvez le faire comme ceci:

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

Utilisation:

var sedan = new Sedan();
sedan.NumberOfDoors = 4;
var auto = ConvertAuto<Auto>(sedan);

Non, voir cette question que j'ai demandé - .NET en utilisant transtypage ascendant génériques

La meilleure façon est de faire un constructeur par défaut de la classe, construire et ensuite appeler une méthode Initialise

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top