8
votes

Héritage et champs statiques partagés

Supposons que j'ai une classe abstrait générique qui fournit une fonctionnalité de constructeur par défaut afin de ne pas avoir à répéter le code dans vos classes héritées: xxx

maintenant le problème est que lorsque j'écris quand j'écris Deux classes héritées comme: xxx

Comme vous pouvez le constater que ces deux sont vraiment des énumérations de type-sécurité. Ils ne fournissent que des valeurs prédéfinies et elles ne peuvent pas être instanciées à d'autres fins.

Question

Le problème est que ces deux classes héritées utilisent les mêmes types génériques avec la classe de base qui est base . Et la manière dont les classes génériques et les champs statiques fonctionnent sont que pour chaque type générique (ou combinaison lorsque plusieurs) un nouveau champ statique est créé.

dans mon cas, la combinaison est String, String C'est pourquoi il n'y a que One Class Static Champ statique et il contient 4 valeurs au lieu d'avoir Deux champs statiques chacun avec 2 valeurs.

Comment-je surmonter ce problème et séparer ceux-ci tandis que maintiennent toujours cette fonctionnalité sur la classe de base donc je ne répète pas mon code?

changeant champ De statique à l'instance ne fonctionnera pas, car ils me retrouverai avec 4 instances qui détiennent une seule valeur ...


5 commentaires

C'est pourquoi le champ statique de la classe de base contient 4 valeurs au lieu de chacun des 2 champs statiques portant chacun 2 valeurs. attendre, où : base finit-il avec 4 valeurs?


@Jcolebrand: Eh bien, car ils héritent de base et la manière dont les classes génériques et les champs statiques fonctionnent, cela signifie qu'un seul champ statique est créé. Et c'est-à-dire pour la base . Toutes les valeurs sont donc sauvegardées, même s'il existe deux classes héritantes.


Même si tu es instanciant une nouvelle chaque fois? Je pensais que la sémantique a indiqué que la statique serait statique sur deux ou statique sur un , pas statique sur base . Je suppose que c'est un vrai problème que vous essayez de surmonter. Pourquoi ne pouvez-vous pas déplacer ceux-ci dans une autre sous-classe qui est une propriété à ce sujet?


@jcolebrand Les 4 éléments proviennent de la construction de deux instances de deux et de deux instances de un , un et deux Les deux regardent les mêmes valeurs statiques .


@Jcolebrand: Pourquoi n'écrivez-vous pas une réponse et fournissez un certain code, car je suis déjà crosseyed à cause de ce problème ... :)


5 Réponses :


9
votes

Cela fonctionnera. Je crois que c'est connu comme le modèle de modèle curieusement récurrent, mais ne me cite pas sur ce xxx

la partie "curieuse" est que les définitions de un et < Code> Deux sont autorisés à se servir de paramètres de type à eux-mêmes!


5 commentaires

Il s'appuie sur des personnes spécifiant le correctement, cependant.


Oui, c'est un piratage, mais il est important de noter qu'il n'applique rien .


@ROBERT: Quelle boucle parlez-vous?


@Groo: Celui-ci: Base , Twkey, Tvalue>, Thkey, Tvalue> Parce que je ne pensais pas à utiliser le troisième paramètre de type générique avec contrainte mais plutôt classe concrète ...


@Groo: Très bon lien! En fait, je parlais de la même boucle infinie qui est mentionnée dans l'article en tant que référence circulaire. Je n'ai pas pu me cacher la tête. :)



0
votes

rendre votre base diffère.

public abstract class Base<T, TKey, TValue> where T : Base<T, TKey, TValue>
{ 
    private static readonly IDictionary<TKey, T> Values = 
        new Dictionary<TKey, T>(); 

    public TKey Key { get; private set; } 
    public TValue Value { get; private set; } 

    protected Base(TKey key, TValue value) 
    { 
        this.Key = key; 
        this.Value = value; 

        Values.Add(key, (T)this); 
    } 
} 


5 commentaires

Merci kris. Je vais vous remettre, parce que je ne sais pas non plus pourquoi cela a été évité. Surtout parce que vous avez simplifié la définition de dictionnaire. Il enregistre t instances ... donc +1 de moi.


Hmmm. Mais si vous utilisez un identificateur , vous ne pouvez pas faire valeurs.add (touche, ceci) ...


En effet, ça m'a raté. Eh bien, dans ce cas, nous devons changer la définition du dictionnaire. Je garderais toujours la contrainte cependant. Je vais mettre à jour la réponse, bien que je puisse la supprimer car il est maintenant très proche de l'autre.


Pas vraiment .. Depuis que je vais ajouter des instances, il est prudent de la modifier à: valeurs.add (clé, ceci comme t) . Et il fonctionne.


True encore, sauf pour l'opérateur comme ; J'utiliserais une distribution explicite.



6
votes

Le problème, bien sûr, se pose car vos deux classes partagent la même classe de base avec les mêmes arguments de type générique. Cela signifie qu'ils partagent les mêmes membres statiques.

Comment puis-je remédier à ce problème et séparer ceux-ci tout en conservant cette fonctionnalité sur la classe de base, donc je ne répète pas mon code?

Une approche serait de créer votre dictionnaire dans un dictionnaire basé sur le type d'exécution actuel: xxx

Ceci fait que toutes les recherches soient 2 phases, Comme vous devez rechercher le type actuel puis la valeur de ce dictionnaire, mais il ne s'appuie pas sur la modification de votre API de sous-classe.


12 commentaires

+1 C'est ce que je voulais mentionner. En réalité, ce dictionnaire statique peut faire partie d'une classe générique complètement différente avec le type de classes héritées en tant que paramètre.


Il convient de noter que cela constituerait toujours un dictionnaire statique par type générique résolu, il suffit de saisir différemment.


@AdamHouldsworthhorth Oui - Bien que maintenant, il est clément à la sous-classe, de sorte que vous obtiendrez "efficacement" ce que l'OP veut. C'est un hack, mais il répond directement à la question de l'OP


@Reedcopsey Oui, je suis d'accord, je clarifiais simplement la différence entre cela et l'autre réponse qui entraînerait deux variables statiques car les types génériques résolvent différemment.


Je pensais à la mettre en œuvre aussi, mais au lieu d'avoir des dictionnaires imbriqués, je concaténate Type avec la clé, car j'utilise des cordes comme des clés ...


@ROBERTKORITNIK Votre hiérarchie de classe suggère d'autres choses peuvent être des clés, cependant. Si ce n'est pas le cas, vous pouvez simplifier cela dramatiquement ...


@REEDCOPSEY: Ils sont, mais depuis que j'étais coincé et pour le moment où mes clés ne seraient que des cordes, je m'amusais avec l'idée ... mais dans la réalité, ces clés peuvent réellement être n'importe quel type que commutateur La déclaration peut mâcher ... La documentation dit qu'il peut être l'une des opérations suivantes: Sbyte, octet, court, Ushort, Int, Uint, Long, Ulong, Char, String ou un Enum-Type . Mais je ne peux pas faire de contrainte à ce sujet, car il s'agit d'un mélange de valeurs et de types de référence ... La chose est que j'ai réussi à écrire une classe abstraite de base pour les énumérations C # Safe-Safe avec la coulée explicite et la conversion intégrale implicite.


@RobertKoritnik Alors ouais, la fixant à une chaîne n'est probablement pas aussi élégante que de lui permettre d'être n'importe quoi. Avec plus d'informations sur ce que vous faites, il peut y avoir une meilleure approche, bien que


Je vais tout mettre dans un Publication du blog plus tard de toute façon. D'autres développeurs peuvent également trouver utile.


@REEDCOPSEY: Qu'est-ce que je fais? J'ai écrit une classe de base enum de type-Safe enum, c'est-à-dire Classe abstraite Public Typeafeenum TVALUE < / code> est le type de sous-couche qui ne se limite pas aux types intégraux tels que Enum est. Et TNAME est le type pour les membres de l'énumération. Habituellement, une chaîne , mais ne leur est pas limitée. Peut être n'importe quoi, DateTime , objets prédéfinis ... Tout ce dont vous avez besoin. Si vous utilisez des types hérités dans les instructions commutateurs , alors TVALUE doit être un type intégral, une chaîne ou une énumération.


@ROBERTKORITNIK Si c'est votre objectif entier, pourquoi ne pas simplement utiliser un dictionnaire comme-est? Ceci est juste une couche supplémentaire d'abstraction qui ne vous achète pas beaucoup ...


@Reedcopsey: Pas vraiment parce que j'ai aussi des opérateurs implicites et explicites définis sur le type afin que vous puissiez jeter explicitement sur ces énums et les utiliser directement dans la classe de commutation. Ce qui m'achète, c'est que l'utilisation de ces énumes est identique à des types d'énumération normaux.



0
votes

Ceci est par conception. Un membre statique n'aura qu'un seul copie dans toutes les instances et un membre statique d'une classe de base n'aura qu'une copie dans toutes les instances de toutes ses sous-classes.

Pourquoi es-tu statique? Voulez-vous réellement que tous les cas de partagent un dictionnaire commun?

Si vous voulez que vous soyez partager sur ceux, mais que vous ne partagez pas avec deux, vous devrez changer votre conception. Soit ne le rend pas statique (qui ne causera aucun partage) ou de le rendre statique au niveau de chaque sous-classe.

Faire une méthode / une propriété abstraite dans la classe de base et remplacer dans les sous-classes si vous souhaitez garder l'API la même entre eux


0 commentaires

2
votes

Cela ne répond pas directement à la question, mais fournit plutôt une conception alternative pour accéder au même objectif.

Il me semble que vous essayez d'utiliser une classe de base pour quelque chose Ce n'est pas le comportement de base, ni au moins de comportement statique.

Séparez les préoccupations d'être une liste de recherche élément et de fournir la référence de la liste de recherche.

Si vous essayez de fournir des recherches connues, je ferais la référence de la liste de recherches séparée des éléments de recherche (comme dans, pas un membre statique): xxx

Ensuite, chaque type peut simplement pointer sur le dictionnaire correspondant, le nommer et l'accéder via une réflexion ou fournir une fonction de fonctionnement: xxx

Ceci vous laissera également séparer La liste elle-même des éléments de la liste (via l'injection peut-être).

Notez également que si les instances de la classe de base conservent une référence aux mêmes dictionnaires, il n'y aura pas besoin d'avoir le membre statique. < / p>


0 commentaires