Étant donné plusieurs instances de la même classe qui ont des propriétés différentes d'un emum commun, comment puis-je créer un dictionnaire avec les clés de l'énumération donnée qui n'autorisera qu'une classe contenant la valeur d'énumération correspondant à cette clé? Par exemple, étant donné le code suivant:
public enum Slot {Head, Torso, Legs} public class Armor { Slot slot; }
Comment pourrais-je créer un dictionnaire qui a donné une clé de Slot.Torso
n'autorisera qu'une valeur qui est un Classe d'armure
avec une propriété slot
de Slot.Torso
.
Si ce n'est pas un dictionnaire, y a-t-il un autre moyen d'exprimer cela en C #?
Merci
3 Réponses :
Créez une énumération avec les noms de classe que vous souhaitez ajouter à la liste blanche. Utilisez ensuite la réflexion pour créer une instance de ceux-ci et comparez leur type avec le type de la propriété. Votre propriété peut être de type objet et vous pouvez alors soit la convertir dans le type de classe en cours de test, soit vérifier son type actuel.
Quelque chose dans ce sens:
public enum WhitelistedTypes { Head = 1, Torso = 2, Legs = 3, } var armorInstance = new Armor(); var asm = typeof(Armor).Assembly; foreach (var item in Enum.GetValues(typeof(WhitelistedTypes))) { var typeName = ((WhitelistedTypes)item).ToString(); var objectType = typeof(Armor).Assembly.CreateInstance("SlotNamespace." + typeName).GetType(); if(armorInstance.Slot.GetType() == objectType) { //Do something } }
Solution possible:
var slots = new Slots(); slots.Add(Slot.Torso, new Torso());
Et une instance comme:
public enum Slot { Head, Torso, Legs } public interface IArmor { Slot SlotType { get; } } public class Torso : IArmor { public Slot SlotType { get => Slot.Torso; } } public class Slots { private Dictionary<Slot, IArmor> _slots { get; set; } = new Dictionary<Slot, IArmor>(); public void Add(Slot slot, IArmor slotClass) { if (slot == slotClass.SlotType) { _slots.Add(slot, slotClass); } else { throw new Exception("Invalid type."); } } }
Je suppose que vous recherchez la sécurité lors de la compilation. Par conséquent, je suggère d'utiliser des génériques pour résoudre ce problème.
Définissez une classe de base pour vos types d'emplacements et leurs implémentations:
var head = new Armor<Head>(); var torso = new Armor<Torso>(); var torsoKey = new Torso(); var dictionary = new SafeDictionary(); dictionary.Add(head.Slot, head); dictionary.Add(torsoKey, torso); Console.WriteLine(torso == dictionary[torsoKey]); // Do not compile: // dictionary.Add(torsoKey, head); // dictionary.Add<Torso>(torsoKey, head);
Votre implémentation Armor pourrait alors ressembler à ceci :
public sealed class SafeDictionary { private readonly Dictionary<Slot, Armor> _dictionary = new Dictionary<Slot, Armor>(); public Armor this[Slot slot] => _dictionary[slot]; public void Add<TSlot>(TSlot slot, Armor<TSlot> armor) where TSlot : Slot, new() { _dictionary.Add(slot, armor); } }
Le bit pertinent est que vous autorisez uniquement la création d'instances génériques avec un type spécifique d'emplacement. La nouvelle contrainte () n'a été ajoutée que pour garder mon exemple de code court.
Vous êtes maintenant prêt à créer votre implémentation restrictive et sécurisée au moment de la compilation d'un dictionnaire pour les instances Armor en utilisant n'importe quel Slot comme clé: p>
public abstract class Armor { } public sealed class Armor<TSlot> : Armor where TSlot : Slot, new() { public TSlot Slot { get; } public Armor() => Slot = new TSlot(); }
L'utilisation est gratuite en ce qui concerne les paires clé-valeur que vous ajoutez:
public abstract class Slot { } public sealed class Head : Slot { } public sealed class Torso : Slot { } public sealed class Legs : Slot { }
Cela vous permet d'utiliser n'importe quel nombre de clés (Slots) avec n'importe quel nombre de valeurs (Armures). Selon vos besoins, il n'est pas possible d'ajouter une instance d'armure avec un emplacement de tête à l'aide d'une clé Torso. (Et toute autre combinaison incompatible). Le prix que vous payez est la nécessité d'une interface de marqueur (Slot, ici en tant que classe abstraite) et dans cet exemple une implémentation vide d'Armor. Si vous souhaitez récupérer la propriété Slot sur Armor, cela peut être fait en ajoutant une interface IArmor. Il y aurait également des options pour restreindre les instances Head, Torso et Legs aux singletons pour se rapprocher de votre exemple d'utilisation d'énumérations, mais c'est hors de portée ici.
J'espère que la solution ci-dessus vous donnera suffisamment de matériel pour résoudre votre problème et développer la solution qui correspond le mieux à vos besoins.
@James Wilkinson: J'ai vu que vous aviez posté un commentaire sur ma réponse. Il semble que vous l'avez déjà supprimé. J'essaierai de répondre à ce dont je me souviens. Vous avez dit que vous aviez des erreurs de compilation. En principe, c'est ce que cherche ma solution: fournir une conception qui permet au compilateur de détecter les erreurs au lieu de les détecter uniquement au moment de l'exécution. Si vous êtes nouveau dans les génériques, il est normal de rencontrer au début toutes sortes d'erreurs de compilation, que vous n'avez peut-être pas rencontrées auparavant. Mais en fait, c'est exactement ce que nous recherchons: nous (je) ne veux pas que le compilateur détecte les erreurs à ma place.
Ce qui précède n'est qu'une conception squelette, il est donc encore incomplet. Si vous le souhaitez, vous pouvez ajouter des exigences plus spécifiques. Ensuite, nous pourrions essayer d'affiner un peu plus le design. Une question que je me poserais est de savoir si c'était votre intention de ne permettre que trois instances d'Armor à ajouter au dictionnaire? (un pour chaque type d'emplacement) Ou aimeriez-vous pouvoir ajouter un nombre arbitraire d'instances d'armure? BTW: Les génériques prennent un certain temps à s'habituer. Mais une fois parfaitement maîtrisé, je ne voudrais plus m'en passer. Le code est tellement plus expressif et puissant ... (j'utilise très rarement des énumérations ...)
Utilisez simplement un dictionnaire normal et vérifiez la clé et la valeur avant d'ajouter une entrée?