Как создать словарь, который допускает только объекты, которые содержат определенное значение, соответствующее ключу словаря - PullRequest
0 голосов
/ 06 февраля 2020

Учитывая несколько экземпляров одного и того же класса, которые имеют разные свойства общего emum, как я могу создать словарь с ключами данного enum, который будет разрешать только класс, содержащий значение enum, соответствующее этому ключу? Например, с помощью следующего кода:

public enum Slot {Head, Torso, Legs}

public class Armor
{
   Slot slot;
}

Как создать словарь, для которого задан ключ Slot.Torso, допускается только значение класса Armor со свойством slot Slot.Torso.

Если не словарь, есть ли другой способ express это в C#?

Спасибо

Ответы [ 3 ]

1 голос
/ 06 февраля 2020

Возможное решение:

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

И, например,:

var slots = new Slots();
slots.Add(Slot.Torso, new Torso());
1 голос
/ 06 февраля 2020

Полагаю, вы ищете безопасность во время компиляции. Поэтому я предлагаю использовать обобщенные методы для решения этой проблемы.

Определите базовый класс для типов ваших слотов и их реализаций:

public abstract class Slot { }

public sealed class Head : Slot { }
public sealed class Torso : Slot { }
public sealed class Legs : Slot { }

Ваша реализация Armor может выглядеть примерно так:

public abstract class Armor { }

public sealed class Armor<TSlot> : Armor
    where TSlot : Slot, new()
{
    public TSlot Slot { get; }
    public Armor() => Slot = new TSlot();
}

Соответствующим битом является то, что вы разрешаете создавать только экземпляры generi c с указанным типом слота c. Ограничение new () было добавлено только для того, чтобы мой пример кода был коротким.

Теперь вы готовы создать ограничительную и безопасную во время компиляции реализацию словаря для экземпляров Armor, используя любой слот в качестве ключа:

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

Использование в зависимости от того, какие пары значений ключей вы добавляете, бесплатное:

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

Это позволяет вам использовать любое количество ключей (слотов) с любым количеством значений (броней). По вашему требованию невозможно добавить экземпляр Armor со слотом Head, используя ключ Torso. (И любая другая несовместимая комбинация). Цена, которую вы платите, это необходимость одного интерфейса маркера (слот, здесь как абстрактный класс) и в этом примере пустой реализации Armor. Если вы хотите восстановить свойство Slot в Armor, это можно сделать, добавив интерфейс IArmor. Также были бы возможности ограничить экземпляры Head, Torso и Legs синглетами, чтобы приблизиться к вашему примеру использования перечислений, но здесь это выходит за рамки.

Я надеюсь, что приведенное выше решение даст вам достаточно материала для решения вашей проблемы и разработки решения, которое наилучшим образом соответствует вашим потребностям.

0 голосов
/ 06 февраля 2020

Создайте перечисление с именами классов, которые вы хотите добавить в белый список. Затем используйте рефлексию, чтобы создать их экземпляр, и сравните их тип с типом свойства. Ваше свойство может иметь тип object , а затем вы можете либо привести его к типу тестируемого класса, либо проверить его текущий тип.

Что-то в этом роде:

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
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...