Une manière de convertir un type de base en un type dérivé

Je ne suis pas sûr que ce soit une chose étrange à faire ou pas, ou si c’est comme si le code sentait … mais je me demandais s’il y avait un moyen (une sorte de motif oop serait bien) de “lancer” un type de base à une forme de son type dérivé. Je sais que cela n’a pas beaucoup de sens car le type dérivé aura des fonctionnalités supplémentaires que le parent n’offre pas, ce qui en soi n’est pas fondamentalement sain. Mais y a-t-il un moyen de le faire? Voici un exemple de code pour que je puisse mieux expliquer ce que je demande.

public class SomeBaseClass { public ssortingng GetBaseClassName {get;set;} public bool BooleanEvaluator {get;set;} } public class SomeDerivedClass : SomeBaseClass { public void Insert(SqlConnection connection) { //...random connection stuff cmd.Parameters["IsItTrue"].Value = this.BooleanEvalutar; //... } } public static void Main(object[] args) { SomeBaseClass baseClass = new SomeBaseClass(); SomeDerivedClass derClass = (SomeDerivedClass)baseClass; derClass.Insert(new sqlConnection()); } 

Je sais que cela semble bizarre mais y a-t-il un moyen d’accomplir quelque chose de ce genre?

Pas à voix haute, dans les langues “gérées”. Il s’agit d’un processus de downcasting , et il n’ya pas de moyen raisonnable de le gérer, exactement pour la raison que vous avez décrite (les sous-classes fournissent plus que les classes de base – d’où cela vient-il?). Si vous voulez vraiment un comportement similaire pour une hiérarchie particulière, vous pouvez utiliser des constructeurs pour les types dérivés qui prendront le type de base comme prototype.

On pourrait construire quelque chose avec la reflection qui traitait les cas simples (types plus spécifiques qui n’ont pas d’état d’addition). En général, redessinez simplement pour éviter le problème.

Edit: Woops, ne peut pas écrire d’opérateurs de conversion entre les types base / dérivés. Une singularité de Microsoft essayant de vous “protéger” contre vous-même. Eh bien, au moins ils ne sont pas aussi mauvais que Sun.

Essayez la composition plutôt que l’inheritance!

Il me semble que vous feriez mieux de passer une instance de SomeBaseClass à SomeDerivedClass (qui ne dériverait plus la classe de base et devrait être renommée en tant que telle)

 public class BooleanHolder{ public bool BooleanEvaluator {get;set;} } public class DatabaseInserter{ BooleanHolder holder; public DatabaseInserter(BooleanHolder holder){ this.holder = holder; } public void Insert(SqlConnection connection) { ...random connection stuff cmd.Parameters["IsItTrue"].Value = holder.BooleanEvalutar; ... } } public static void Main(object[] args) { BooleanHolder h = new BooleanHolder(); DatabaseInserter derClass = new DatabaseInserter(h); derClass.Insert(new sqlConnection); } 

Découvrez http://www.javaworld.com/javaworld/jw-11-1998/jw-11-techniques.html (page 3):

Réutilisation du code via composition La composition offre à Apple un moyen alternatif de réutiliser l’implémentation de peel () de Fruit. Au lieu d’étendre Fruit, Apple peut contenir une référence à une instance Fruit et définir sa propre méthode peel () qui invoque simplement peel () sur le Fruit.

Personnellement, je ne pense pas que cela vaille la peine d’utiliser l’inheritance dans ce cas. Au lieu de cela, passez simplement l’instance de la classe de base dans le constructeur et accédez-y via une variable membre.

 private class ExtendedClass //: BaseClass - like to inherit but can't { public readonly BaseClass bc = null; public ExtendedClass(BaseClass b) { this.bc = b; } public int ExtendedProperty { get { } } } 

Le downcasting a du sens, si vous avez un object de classe dérivée mais qu’il est référencé par une référence de type de classe de base et que, pour une raison quelconque, vous souhaitez qu’il soit référencé par une référence de type de classe dérivée. En d’autres termes, vous pouvez réduire à néant l’effet de la hausse précédente. Mais vous ne pouvez pas avoir un object de classe de base référencé par une référence d’un type de classe dérivé.

Je ne dis pas que je recommande ceci. Mais vous pouvez transformer la classe de base en chaîne JSON, puis la convertir en classe dérivée.

 SomeDerivedClass layer = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(BaseClassObject)); 

Non, ce n’est pas possible. Dans un langage géré comme C #, cela ne fonctionnera tout simplement pas. Le runtime ne le permettra pas, même si le compilateur le laisse passer.

Vous avez dit vous-même que cela semble bizarre:

 SomeBaseClass class = new SomeBaseClass(); SomeDerivedClass derClass = (SomeDerivedClass)class; 

Alors posez-vous la question de savoir si class est en fait une instance de SomeDerivedClass ? Non, alors la conversion n’a aucun sens. Si vous devez convertir SomeBaseClass en SomeDerivedClass , vous devez alors fournir une sorte de conversion, soit un constructeur, soit une méthode de conversion.

Il semblerait que votre hiérarchie de classe nécessite un certain travail. En général, il ne devrait pas être possible de convertir une instance de classe de base en instance de classe dérivée. Il devrait généralement y avoir des données et / ou des fonctionnalités qui ne s’appliquent pas à la classe de base. Si la fonctionnalité de la classe dérivée s’applique à toutes les instances de la classe de base, elle doit soit être intégrée à la classe de base, soit extraite dans une nouvelle classe ne faisant pas partie de la hiérarchie des classes de base.

Le langage C # ne permet pas de tels opérateurs, mais vous pouvez toujours les écrire et ils fonctionnent:

 [System.Runtime.ComstackrServices.SpecialName] public static Derived op_Implicit(Base a) { ... } [System.Runtime.ComstackrServices.SpecialName] public static Derived op_Explicit(Base a) { ... } 

Oui, c’est une odeur de code, et le fait que votre chaîne d’inheritance soit brisée est quasiment inexistante.

Je suppose (à partir de l’échantillon limité) que vous préférez que DerivedClass opère sur une instance de SomeBaseClass – de sorte que “DerivedClass ait une classe SomeBaseClass”, plutôt que “DerivedClass est une SomeBaseClass”. Ceci est connu comme “favoriser la composition sur l’inheritance”.

Comme d’autres l’ont noté, le casting que vous proposez n’est pas vraiment possible. Serait-ce peut-être un cas où le modèle Décorateur (Extrait Premier Head) peut être introduit?

Avez-vous pensé à une interface que seraient actuellement implémentées votre classe de base et votre classe dérivée? Je ne connais pas les détails de la mise en œuvre de cette manière, mais cela pourrait fonctionner.

Cela s’appelle le downcasting et la suggestion de Seldaek d’utiliser la version “safe” est saine.

Voici une description assez correcte avec des exemples de code .

Ce n’est pas possible car comment allez-vous obtenir le “extra” que possède la classe dérivée. Comment le compilateur saura-t-il que vous entendez déroulé dérivéClass1 et non dérivéClass2 lorsque vous l’instanciez?

Je pense que ce que vous cherchez vraiment est le motif d’usine ou similaire, vous pouvez donc instancier des objects sans vraiment connaître le type explicite qui est en train d’être instancié. Dans votre exemple, avoir la méthode “Insert” serait une interface que l’instance retourne implémente par la fabrique.

Je ne sais pas pourquoi personne n’a dit cela et j’ai peut-être raté quelque chose mais vous pouvez utiliser le mot-clé as et si vous devez faire une instruction if si.

 SomeDerivedClass derClass = class as SomeDerivedClass; //derClass is null if it isnt SomeDerivedClass if(class is SomeDerivedClass) ; 

-edit- J’ai posé cette question il y a longtemps

J’ai récemment eu besoin d’étendre un DTO simple avec un type dérivé afin d’y append d’autres propriétés. J’ai ensuite voulu réutiliser une logique de conversion que j’avais, des types de bases de données internes aux DTO.

La façon dont je l’ai résolue était en imposant un constructeur vide sur les classes DTO, en l’utilisant comme ceci:

 class InternalDbType { public ssortingng Name { get; set; } public DateTime Date { get; set; } // Many more properties here... } class SimpleDTO { public ssortingng Name { get; set; } // Many more properties here... } class ComplexDTO : SimpleDTO { public ssortingng Date { get; set; } } static class InternalDbTypeExtensions { public static TDto ToDto(this InternalDbType obj) where TDto : SimpleDTO, new() { var dto = new TDto { Name = obj.Name } } } 

Je peux ensuite réutiliser la logique de conversion du simple DTO lors de la conversion au complexe. Bien sûr, je vais devoir remplir les propriétés du type complexe d’une autre manière, mais avec de nombreuses propriétés du DTO simple, cela simplifie vraiment les choses.

Cela ne peut pas fonctionner. Allez voir la page d’aide liée par l’erreur de compilation.

La meilleure solution consiste à utiliser les méthodes d’usine ici.

Comme beaucoup de réponses l’ont fait remarquer, vous ne pouvez pas baisser les prix, ce qui est tout à fait logique.

Cependant, dans votre cas, SomeDerivedClass n’a pas de propriétés qui seront “manquantes”. Vous pouvez donc créer une méthode d’extension comme celle-ci:

 public static T ToDerived(this SomeBaseClass baseClass) where T:SomeBaseClass, new() { return new T() { BooleanEvaluator = baseClass.BooleanEvaluator, GetBaseClassName = baseClass.GetBaseClassName }; } 

Donc, vous ne lancez pas, simplement en convertissant:

 SomeBaseClass b = new SomeBaseClass(); SomeDerivedClass c = b.ToDerived(); 

Cela ne fonctionne vraiment que si toutes les données de la classe de base se présentent sous la forme de propriétés lisibles et inscriptibles.

C ++ le gère en utilisant un constructeur. C ++ Typecasting . Cela me semble un oubli. Beaucoup d’entre vous ont soulevé la question de savoir ce que le processus ferait avec les propriétés supplémentaires. Je répondrais, que fait le compilateur lorsqu’il crée la classe dérivée lorsque le programmeur ne définit pas les propriétés? J’ai géré cette situation similaire à C ++. Je crée un constructeur qui prend la classe de base puis définit manuellement les propriétés dans le constructeur. Ceci est certainement préférable à la définition d’une variable dans la classe dérivée et à la rupture de l’inheritance. Je le choisirais aussi par une méthode d’usine car je pense que le code résultant serait plus propre.