Question

Je ne suis pas sûr que ce soit une chose étrange à faire ou non, ou si c'est quelque chose qui sent le code ... mais je me demandais s'il y avait un moyen (une sorte de motif oop serait bien) de " cast " un type de base à une forme de son type dérivé. Je sais que cela n’a aucun sens car le type dérivé aura des fonctionnalités supplémentaires que le parent ne propose pas, ce qui en soi n’est pas fondamentalement valable. Mais y a-t-il un moyen de faire cela? Voici un exemple de code pour que je puisse mieux expliquer ce que je vous demande.

public class SomeBaseClass {
    public string 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 maladroit, mais y a-t-il un moyen d'accomplir quelque chose de ce genre?

Était-ce utile?

La solution

Pas bien, dans " géré " langues. Ceci est un downcasting , et il n’existe aucun moyen sain de le gérer, pour la raison que vous avez décrite (les sous-classes fournissent plus que des classes de base - d’où provient ce "plus"?). Si vous souhaitez réellement 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 peut construire quelque chose avec réflexion qui traite les cas simples (types plus spécifiques n’ayant pas d’état d’addition). En général, il suffit de revoir la conception pour éviter le problème.

Éditer: Woops, impossible d'écrire des opérateurs de conversion entre types de base / dérivés. Une bizarrerie de Microsoft essayant de vous "protéger" contre toi-même. Eh bien, au moins ils ne sont pas aussi mauvais que Sun.

Autres conseils

Essayez la composition au lieu de l'héritage!

Il me semble que vous feriez mieux de passer une instance de SomeBaseClass à SomeDerivedClass (qui ne dérive plus de la classe de base et doit être renommée comme 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 de code via composition Composition   fournit un moyen alternatif pour Apple   réutiliser la mise en œuvre de Fruit de   peler(). Au lieu d'étendre Fruit,   Apple peut contenir une référence à un fruit   instance et définir sa propre peau ()   méthode qui appelle simplement peel () sur   le fruit.

Personnellement, je ne pense pas que cela vaille la peine d'utiliser l'héritage 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 objet de classe dérivée mais qu'il est référencé par une référence de type classe de base et pour une raison quelconque, vous souhaitez qu'il soit de nouveau référencé par une référence de type de classe dérivée. En d'autres termes, vous pouvez downcast pour inverser l'effet de la conversion en amont précédente. Mais vous ne pouvez pas avoir un objet de classe de base référencé par une référence de type classe dérivée.

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<SomeDerivedClass>(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 maladroit:

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

Alors demandez-vous si class est en réalité une instance de SomeDerivedClass ? Non, alors la conversion n'a aucun sens. Si vous devez convertir SomeBaseClass en SomeDerivedClass , vous devez fournir une sorte de conversion, soit un constructeur, soit une méthode de conversion.

Toutefois, il semble que votre hiérarchie de classe ait besoin de travail. En général, il ne devrait pas pouvoir convertir une instance de classe de base en une 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 classe dérivée s'applique à toutes toutes les occurrences de la classe de base, elle doit être soit cumulée dans la classe de base, soit extraite d'une nouvelle classe ne faisant pas partie de la hiérarchie de la classe de base.

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

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

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

Oui, il s’agit d’une odeur de code et permet de comprendre le fait que votre chaîne d’héritage est rompue.

Mon devine (à partir de l'échantillon limité), c'est que vous préféreriez que DerivedClass fonctionne sur une instance de SomeBaseClass - de sorte que "DerivedClass ait une SomeBaseClass", plutôt que "DerivedClass est une SomeBaseClass". C’est ce que l’on appelle la "composition privilégiée par rapport à l’héritage".

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

Avez-vous pensé à une interface que votre classe de base et votre classe dérivée implémenteraient actuellement? Je ne connais pas les raisons pour lesquelles vous implémentez cette méthode, mais cela pourrait fonctionner.

C’est ce qu’on appelle le downcasting et la suggestion de Seldaek d’utiliser le paramètre "safe". la version est bonne.

Voici une description avec exemples de code .

Cela n’est pas possible car comment allez-vous obtenir le " extra " que la classe dérivée a. Comment le compilateur sait-il que vous voulez dire dérivéeClass1 et non pas dérivéeClass2 lorsque vous l'instanciez?

Je pense que ce que vous recherchez réellement, c'est le modèle d'usine ou similaire, de sorte que vous puissiez instancier des objets sans vraiment connaître le type explicite à instancier. Dans votre exemple, l'option "Insérer" méthode serait une interface que l’instance renvoyée par l’usine retournera implémente.

Je ne sais pas pourquoi personne ne l'a dit et il se peut que je manque quelque chose, mais vous pouvez utiliser le mot-clé as et si vous devez utiliser 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 de lui attribuer davantage de propriétés. Je souhaitais ensuite réutiliser une logique de conversion que j'avais, des types de base de données internes aux DTO.

J'ai résolu le problème en appliquant un constructeur vide sur les classes DTO, en l'utilisant comme suit:

class InternalDbType {
    public string Name { get; set; }
    public DateTime Date { get; set; }
    // Many more properties here...
}

class SimpleDTO {
    public string Name { get; set; }
    // Many more properties here...
}

class ComplexDTO : SimpleDTO {
    public string Date { get; set; }
}

static class InternalDbTypeExtensions {
    public static TDto ToDto<TDto>(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 en complexe. Bien sûr, je vais devoir renseigner les propriétés du type complexe d’une autre manière, mais avec beaucoup, beaucoup de propriétés du simple DTO, cela simplifie vraiment les choses à l’OMI.

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

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

Comme de nombreuses réponses l’ont souligné, vous ne pouvez pas dédramatiser, 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<T>(this SomeBaseClass baseClass) 
    where T:SomeBaseClass, new()
{
    return new T()
    {
        BooleanEvaluator = baseClass.BooleanEvaluator,
        GetBaseClassName = baseClass.GetBaseClassName
    };
}

Donc, vous ne lancez pas, vous convertissez simplement:

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

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

C ++ le gère en utilisant un constructeur. Conversion en C ++ . 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 alors que 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 définitivement préférable à la définition d'une variable dans la classe dérivée et à la suppression de l'héritage. Je le choisirais également par rapport à une méthode d'usine, car je pense que le code obtenu aurait une apparence plus propre.

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