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


88

Est-il possible d'affecter un objet de classe de base à une référence de classe dérivée avec un typage explicite en C #?.

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

Réponses:


98

Non. Une référence à une classe dérivée doit en fait faire référence à une instance de la classe dérivée (ou null). Sinon, comment vous attendriez-vous qu'il se comporte?

Par exemple:

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

Si vous souhaitez pouvoir convertir une instance du type de base en type dérivé, je vous suggère d'écrire une méthode pour créer une instance de type dérivé appropriée. Ou regardez à nouveau votre arbre d'héritage et essayez de le repenser pour ne pas avoir à le faire en premier lieu.


72
@Mike: Le code se compile très bien. Il tombe au moment de l'exécution cependant :)
Jon Skeet

1
Alors que se passe-t-il exactement lorsque nous écrivons Base b = new Derived (); ? Va-t-il créer des objets pour la classe de base et la classe dérivée?
Ashif Nataliya

3
@Akie: Non, cela crée un seul objet de type Derived, mais vous pouvez traiter une Derivedréférence comme une Baseréférence.
Jon Skeet

Y a-t-il donc une différence d'objet résultant pour ces deux déclarations? Base b = nouvelle base () et base b = nouvelle dérivée ()? quel est l'avantage d'utiliser l'un par rapport à l'autre?
Ashif Nataliya le

4
@Akie: Oui, l'un crée une instance de Base, et l'autre crée une instance de Derived. Si vous appelez une méthode virtuelle sur blaquelle a été remplacée dans Derived, vous verrez le Derivedcomportement si vous avez une instance de Derived. Mais il n'est pas vraiment approprié d'entrer dans les détails dans un fil de commentaires Stack Overflow - vous devriez vraiment lire un bon livre ou un didacticiel C #, car c'est assez fondamental.
Jon Skeet

46

Non, ce n'est pas possible car l'attribuer à une référence de classe dérivée reviendrait à dire "La classe de base est un substitut entièrement capable de la classe dérivée, elle peut faire tout ce que la classe dérivée peut faire", ce qui n'est pas vrai puisque les classes dérivées en offrent généralement plus de fonctionnalités que leur classe de base (du 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 comme paramètre, en copiant les valeurs.

Quelque chose comme ça:

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 obtenez un objet de classe dérivé entièrement fonctionnel 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();//base class
Derived d = new Derived();//derived class

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!

23

J'ai eu ce problème et je l'ai résolu en ajoutant une méthode qui prend un paramètre de type et convertit l'objet actuel 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

Vous devez utiliser le type de la classe actuelle (classe de base) pour obtenir et définir les propriétés, car ce sont les valeurs que vous souhaitez mapper à la classe dérivée.
Bowofola

1
Si vous avez des propriétés qui ne peuvent pas être écrites dans le type dérivé, vous devriez probablement changer en: if (property.CanWrite) property.SetValue (instance, property.GetValue (this, null), null);
user3478586

10

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

J'utilise le code suivant dans les cas malheureux où j'ai besoin d'utiliser un type de base comme type dérivé. Oui, c'est une violation du principe de substitution de Liskov (LSP) et oui la plupart du temps, nous privilégions la composition à l'héritage. Accessoires à Markus Knappen Johansson dont la réponse originale est basée sur cela.

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

Puisqu'il utilise la réflexion, il est "cher". Utilisez en conséquence.


J'ai juste essayé ceci, et je me suis dit que cela pourrait être amélioré, en surchargeant l'opérateur explicite (et l'opérateur implicite également) .. mais - le compilateur ne le permettra pas: user-defined conversions to or from a base class are not allowed je vois les raisons de cela, mais je suis déçu, car cela aurait été tellement amusant si cela avait permis cela ..
Henrik

@MEC: J'ai remarqué que vous avez laissé tomber la partie `where T: MyBaseClass` et que vous avez ajouté la if (type.BaseType != null)déclaration relative au A. de Markus Knappen Johansson. Pourquoi? Cela signifie qu'il autoriserait un type dans les appels qui ne sont pas dérivés de MyBaseClass (ou quoi que ce soit d'ailleurs). Je me rends compte que cela provoquera toujours une erreur du compilateur si Assigné à myDerivedObject, mais s'il est juste utilisé comme Expression, il se compilera et au moment de l'exécution créera simplement un myDerivedObject sans aucune donnée copiée depuis "myBaseObject". Je ne peux pas imaginer un cas d'utilisation pour cela.
Tom

@Tom, réponse tardive, mais a pensé que cela pourrait encore être utile. La meilleure réponse à votre question serait probablement de dire que le nom «As» aurait mieux été «AsOrDefault». Essentiellement, nous pouvons prendre ce résultat et le comparer à un Default comme nous le faisons lorsque nous utilisons SingleOrDefault ou FirstOrDefault de Linq.
MEC le

7

Non, ce n'est pas possible, d'où votre erreur d'exécution.

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


7

Solution avec JsonConvert (au lieu de typecast)

Aujourd'hui, j'ai rencontré le même problème et j'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);

J'ai répondu à nouveau ci-dessous avec des méthodes d'extension. Oui, c'est la réponse.
Patrick Knott le

5

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

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

Il fera la tâche de copier automatiquement les propriétés d'une instance à une autre (pas nécessairement du même type).


3

Expansion sur la réponse de @ ybo - ce n'est pas possible car l'instance que vous avez de la classe de base n'est pas en fait 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 convertir une instance de la classe dérivée en une instance de la classe de base est que la classe dérivée est déjà en fait une instance de la classe de base, puisqu'elle a déjà ces membres. Le contraire ne peut être dit.


3

Vous pouvez convertir une variable typée comme classe de base en type d'une classe dérivée; cependant, par nécessité, cela effectuera une vérification à l'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 modifié (notamment, il peut ne pas avoir la même taille). Cependant, vous pouvez convertir une instance, en créant une nouvelle instance du deuxième type, mais vous devez écrire le code de conversion manuellement.


2

Non ce n'est pas possible.

Considérez un scénario dans lequel un ACBus est une classe dérivée de la classe de base Bus. ACBus a des fonctionnalités telles que TurnOnAC et TurnOffAC qui fonctionnent sur un champ nommé ACState. TurnOnAC définit ACState sur on et TurnOffAC définit ACState sur off. Si vous essayez d'utiliser les fonctionnalités TurnOnAC et TurnOffAC sur Bus, cela n'a aucun sens.


2
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 enfant, l'objet de classe de base est automatiquement lancé afin que la variable de référence de classe de base puisse pointer vers l'objet de classe enfant.

mais pas l'inverse car une variable de référence de classe enfant ne peut pas pointer vers un objet de classe de base car aucun objet de classe enfant n'est créé.

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


2

Il existe en fait un moyen de le faire. Pensez à la façon dont vous pourriez utiliser Newtonsoft JSON pour désérialiser un objet de json. Il ignorera (ou du moins pourra) les éléments manquants et peuplera tous les éléments dont il a connaissance.

Alors, voici comment je l'ai fait. Un petit exemple de code suivra mon explication.

  1. Créez une instance de votre objet à partir de la classe de base et remplissez-la en conséquence.

  2. À l'aide de la classe «jsonconvert» de Newtonsoft json, sérialisez cet objet dans une chaîne json.

  3. Créez maintenant votre objet de sous-classe en désérialisant avec la chaîne json créée à l'étape 2. Cela créera une instance de votre sous-classe avec toutes les propriétés de la classe de base.

Ça fonctionne super bien! Alors ... quand est-ce utile? Certaines personnes ont demandé quand cela aurait du sens et ont suggéré de changer le schéma de l'OP pour tenir compte du fait que vous ne pouvez pas le faire nativement avec l'héritage de classe (dans .Net).

Dans mon cas, j'ai une classe de paramètres qui contient tous les paramètres "de base" d'un service. Les services spécifiques ont plus d'options et ceux-ci proviennent d'une autre table DB, donc ces classes héritent de la classe de base. Ils ont tous un ensemble d'options différent. 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 ce faire avec une seule requête DB. Juste après cela, je crée l'objet de sous-classe en utilisant la méthode décrite ci-dessus. Je fais ensuite une deuxième requête et remplis 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. Répéter ceci pour de nouvelles sous-classes supplémentaires ne prend que quelques lignes de code. C'est simple, et il utilise un package très éprouvé (Newtonsoft) pour faire fonctionner la magie.

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

en utilisant C # et Newtonsoft.Json: var destObject = JsonConvert.DeserializeObject<DestinationType>(JsonConvert.SerializeObject(srcObject));. Je n'utiliserais cela que pour les tests unitaires et autres "piratages" hors production!
thinkOfaNumber

2

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

}

1

Ce n'est peut-être pas pertinent, mais j'ai pu exécuter du code sur un objet dérivé compte tenu de sa base. C'est définitivement plus piraté que je ne le souhaiterais, mais ça marche:

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

1

Vous pouvez le faire en utilisant generic.

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 ne dupliquez pas le code
  2. Vous n'utilisez pas la réflexion (qui est lente)
  3. Toutes vos conversions se trouvent au même endroit

1

Je sais que c'est vieux mais je l'ai utilisé avec succès pendant 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);
        }
    } 

1

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

Oui, c'est simple, non ça ne couvre pas tous les scénarios, oui ça pourrait être élargi et amélioré, non ce n'est pas parfait, oui ça pourrait éventuellement être rendu plus efficace, non ce n'est pas la meilleure chose depuis le pain tranché, oui il y en a mappeurs d'objet de package nuget robustes complets qui sont bien meilleurs pour une utilisation intensive, etc.

Et bien sûr, il essaiera de mapper les valeurs de n'importe quel objet à n'importe quel objet, dérivé ou non (seules les propriétés publiques qui portent le même nom bien sûr - ignorent le reste).

USAGE:

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

CLASSE UTILITAIRE STATIQUE:

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

1

Vous pouvez utiliser un constructeur de copie qui appelle immédiatement le constructeur d'instance, ou si votre constructeur d'instance fait plus que des affectations, le constructeur de copie affecte les valeurs entrantes à l'instance.

class Person
{
    // Copy constructor 
    public Person(Person previousPerson)
    {
        Name = previousPerson.Name;
        Age = previousPerson.Age;
    }

    // Copy constructor calls the instance constructor.
    public Person(Person previousPerson)
        : this(previousPerson.Name, previousPerson.Age)
    {
    }

    // Instance constructor.
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public int Age { get; set; }

    public string Name { get; set; }
}

Référence à la documentation Microsoft C # sous Constructor pour cet exemple ayant eu ce problème dans le passé.


0

Une autre solution consiste à ajouter une 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;
            }
        }

puis avoir 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 écrasera également éventuellement les propriétés de destination si elles sont déjà définies (non nulles) ou non.


0

Est-il possible d'affecter un objet de classe de base à une référence de classe dérivée avec un typage explicite en C #?.

Des conversions non seulement explicites, mais également implicites sont possibles.

Le langage C # n'autorise pas de tels opérateurs de conversion, mais vous pouvez toujours les écrire en utilisant C # pur et ils fonctionnent. Notez que la classe qui définit l'opérateur de conversion implicite ( Derived) et la classe qui utilise l'opérateur ( Program) doivent être définies dans des assemblys séparés (par exemple, la Derivedclasse est dans un library.dllqui est référencé en program.execontenant la Programclasse).

//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 référencez la bibliothèque à l'aide de la référence de projet dans Visual Studio, VS affiche des gribouillis lorsque vous utilisez la conversion implicite, mais il se compile très bien. Si vous faites simplement référence au library.dll, il n'y a pas de gribouillis.


De quelle magie noire s'agit-il?!? Aussi, comment "Derived z = new Base ()" m'aide-t-il à faire "BaseCls baseObj; DerivedCls derivedObj; derivedObj = (DerivedCls) baseObj" (le Q de l'OP)? De plus, que fait l' System.Runtime.CompilerServices.SpecialNameattribut? La documentation pour chaque version de la plus ancienne disponible (2.0) à la "version actuelle" (4.6? "N'importe qui? N'importe qui?") Ne dit pas ce qu'elle fait, mais dit "La classe SpecialNameAttribute n'est pas actuellement utilisée dans le .NET Framework, mais est réservé pour une utilisation future. ". Voir: [lien] ( msdn.microsoft.com/en-us/library/ms146064(v=vs.100).aspx ).
Tom

> "De quelle magie noire s'agit-il?!?" Cela s'appelle .Net Framework (CLR, IL, BCL). L'ensemble des fonctionnalités des langages IL, C # et VB ne sont pas les mêmes. Certaines fonctionnalités de VB ne sont pas prises en charge par C #. Il existe des fonctionnalités dans IL que C # ne prend pas en charge. Il existe des restrictions en C # qui sont plutôt arbitraires et n'existent pas dans l'IL sous-jacent (comme where T : Delegateles propriétés paramétrées ou les indexeurs, etc.).
Ark-kun

> "Aussi, comment" Derived z = new Base () "m'aide-t-il à faire" BaseCls baseObj; DerivedCls derivedObj; DerivedObj = (DerivedCls) baseObj "(le Q de l'OP)?" C'est juste le cas. Cela résout la question du PO. Et vous n'avez même pas besoin du casting explicite.
Ark-kun

> what does System.Runtime.CompilerServices.SpecialName Attribute do?- Il est utilisé pour marquer les méthodes produites par certaines constructions de commodité spéciales des langages .Net de haut niveau: accesseurs de propriété, accesseurs d'événements, constructeurs, opérateurs, indexeurs, etc. À moins que la méthode IL soit marquée, specialnameelle ne serait pas vue comme propriété / événement / constructeur et il serait simplement reconnu comme une méthode normale. Marquer manuellement des méthodes nommées de manière appropriée avec cet attribut ne fait qu'une partie du travail du compilateur.
Ark-kun

VB.Net a un opérateur électrique. C # ne le fait pas. Comment surchargeriez-vous un opérateur de puissance en C # pour une utilisation dans VB.Net? Définissez simplement une op_Exponentméthode et marquez-la avec l' specialnameattribut.
Ark-kun

0

Que diriez-vous:

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

0

La meilleure façon d'ajouter toutes les propriétés de base à l'élément dérivé est d'utiliser la réflexion dans costructor. Essayez ce code, sans créer de méthodes ou d'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) { }
        }

    }

0

Je ne suis pas d'accord pour dire que ce n'est pas possible. 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;
}

Usage:

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

var auto =est toujours de typesedan
bendecko

0

C'est ainsi que j'ai résolu ce problème pour les champs. Vous pouvez faire la même itération à travers les propriétés si vous le souhaitez. Vous voudrez peut-être faire quelques vérifications pour nulletc., mais c'est l'idée.

 public static DerivedClass ConvertFromBaseToDerived<BaseClass, DerivedClass>(BaseClass baseClass)
            where BaseClass : class, new()
            where DerivedClass : class, BaseClass, new()
        {
            DerivedClass derived = (DerivedClass)Activator.CreateInstance(typeof(DerivedClass));
            derived.GetType().GetFields().ToList().ForEach(field =>
            {
                var base_ = baseClass.GetType().GetField(field.Name).GetValue(baseClass);
                field.SetValue(derived, base_);

            });

            return derived;
        }

0

Vous pouvez simplement sérialiser l'objet de base en JSON, puis le désérialiser en objet dérivé.


0

Pas dans le sens traditionnel ... Convertissez en Json, puis en votre objet, et boum, c'est fait! Jesse ci-dessus a publié la réponse en premier, mais n'a pas utilisé ces méthodes d'extension qui facilitent tellement le processus. Créez quelques méthodes d'extension:

    public static string ConvertToJson<T>(this T obj)
    {
        return JsonConvert.SerializeObject(obj);
    }
    public static T ConvertToObject<T>(this string json)
    {
        if (string.IsNullOrEmpty(json))
        {
            return Activator.CreateInstance<T>();
        }
        return JsonConvert.DeserializeObject<T>(json);
    }

Mettez-les dans votre boîte à outils pour toujours, alors vous pouvez toujours le faire:

var derivedClass = baseClass.ConvertToJson().ConvertToObject<derivedClass>();

Ah, la puissance de JSON.

Il y a quelques pièges avec cette approche: nous créons vraiment un nouvel objet, pas un casting, qui peut ou non avoir une importance. Les champs privés ne seront pas transférés, les constructeurs avec des paramètres ne seront pas appelés, etc. Il est possible que certains enfants json ne soient pas affectés. Les flux ne sont pas gérés de manière innée par JsonConvert. Cependant, si notre classe ne s'appuie pas sur des champs et des constructeurs privés, il s'agit d'une méthode très efficace pour déplacer des données d'une classe à l'autre sans mappage ni appel de constructeurs, ce qui est la raison principale pour laquelle nous souhaitons effectuer un cast en premier lieu.


Cela ne fait pas ce qu'OP a demandé. Ce que vous faites est de construire un nouvel objet du type correct pour la variable, en utilisant les données de l'objet d'origine du type incorrect. Cela peut fonctionner ou non, mais dans tous les cas, cela n'affecte certainement pas un objet du type de classe de base à une variable du type dérivé.
Lasse V. Karlsen le

J'ai répondu à la question: est-il possible d'assigner un objet de classe de base à une référence de classe dérivée avec un typage explicite? En disant non. Je propose une alternative qui fonctionne absolument et qui est moins déroutante que les génériques. Comme indiqué à plusieurs reprises ci-dessus, cela peut causer des problèmes d'attribution à une classe dérivée des propriétés d'une classe de base, cependant, c'est exactement comment cela fonctionnerait (et fonctionne dans apis) si c'était possible. Ce n'est pas parce que ma réponse peut être utilisée à partir d'un "mauvais" type qu'elle ne peut pas être utilisée pour un "bon" type. @ LasseV.Karlsen veuillez retirer votre évaluation négative.
Patrick Knott le

Contrairement à la plupart des réponses ici que JsonConverts en guirlande, je montre également comment gérer null.
Patrick Knott le

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.