Подтвердить что ты не робот

В С#: Как объявить общий словарь с типом как ключ и IEnumerable <> этого типа в качестве значения?

Я хочу объявить словарь, который хранит типизированный IEnumerable определенного типа, с этим точным типом как ключ, например: (Отредактировано, чтобы следовать комментариям johny g)

private IDictionary<Type, IEnumerable<T>> _dataOfType where T: BaseClass; //does not compile!

Конкретные классы, которые я хочу сохранить, все вытекают из BaseClass, поэтому идея использовать его как ограничение. Компилятор жалуется, что он ожидает точку с запятой после имени участника.

Если бы это сработало, я бы ожидал, что это сделает последующий поиск из словаря простым:

IEnumerable<ConcreteData> concreteData;
_sitesOfType.TryGetValue(typeof(ConcreteType), out concreteData);

Как определить такой словарь?

4b9b3361

Ответ 1

Вам может и не понадобиться словарь для этого, но это зависит от ваших потребностей. Если вам понадобится только один такой список для каждого типа приложения (например, "словарь" static), следующий шаблон может быть эффективен и хорошо подходит для ввода типа:

interface IBase {}

static class Container {
    static class PerType<T> where T : IBase {
        public static IEnumerable<T> list;
    }

    public static IEnumerable<T> Get<T>() where T : IBase 
        => PerType<T>.list; 

    public static void Set<T>(IEnumerable<T> newlist) where T : IBase 
        => PerType<T>.list = newlist;

    public static IEnumerable<T> GetByExample<T>(T ignoredExample) where T : IBase 
        => Get<T>(); 
}

Обратите внимание, что перед принятием этого подхода следует тщательно подумать о различии между типом времени компиляции и типом времени выполнения. Этот метод с радостью позволит вам сохранить переменную IEnumerable<SomeType>, определенную во время выполнения, как в SomeType, так и при ее использовании под любым из базовых типов SomeType, включая IBase, без какой-либо ошибки времени выполнения или ошибки типа компиляции может быть функцией или ошибкой, ожидающей своего появления, поэтому вам может понадобиться if проверить это.

Кроме того, этот подход игнорирует потоки; поэтому, если вы хотите получить доступ к этой структуре данных из нескольких потоков, вы, вероятно, захотите добавить некоторую блокировку. Ссылки на чтение/запись являются атомарными, поэтому вы не получите коррупции, если вы не заблокируете, но, конечно, возможны устаревшие данные и условия гонки.

Ответ 2

Используйте System.ComponentModel.Design.ServiceContainer, который уже доступен в .NET Framework.

        ServiceContainer container = new ServiceContainer();

        IList<int> integers = new List<int>();
        IList<string> strings = new List<string>();
        IList<double> doubles = new List<double>();

        container.AddService(typeof(IEnumerable<int>), integers);
        container.AddService(typeof(IEnumerable<string>), strings);
        container.AddService(typeof(IEnumerable<double>), doubles);

Ответ 3

Вы не ограничиваете T в частном члене; вы ограничиваете его на уровне класса.

class Foo<T> where T : BaseClass
{
    private IDictionary<T, IEnumerable<T>> _dataOfType;

    public Foo(IDictionary<T, IEnumerable<T>> dataOfType)
    {
        this._dataOfType = dataOfType;
    }   
}   

Ответ 4

  • Вы не можете ограничить определенную переменную. Он работает только по классам и методам. Честно говоря, это действительно не имеет никакого смысла на переменном уровне.
  • Вы хотите создать собственный класс - class WeirdDictionary : IDictionary<Type, IEnumerable>, который будет перегружать метод Add, чтобы взять тип и IEnumerable этого типа, которые вы можете использовать с помощью ограничений, и отбросить IEnumerable < > to IEnumerable. Также перегрузите указатель и верните его соответствующему типу.
    Все это кастинг необходимо, так как generics строгие относительно IEnumerable<Base>, как хорошие, как IEnumerable<Derived> (Это называется дисперсией, я верю?)

Это решение слегка обобщено, так как reuse rocks

Изменить по 280Z28:

Сначала я отметил это, потому что точка № 2 была запутанной, и я неверно истолковал ее. Используя явную реализацию методов в IDictionary<Type, IEnumerable> и предоставляя общие альтернативы, вы можете получить довольно чистый интерфейс. Обратите внимание, что вы не можете создавать родовые индексы, поэтому вам всегда нужно использовать TryGet<T> (что в любом случае является хорошей идеей). Я только включил явную реализацию одного из методов IDictionary<>, чтобы показать, как выполнять проверки. Не выводить WeirdDictionary непосредственно из Dictionary<Type, IEnumerable> или вы потеряете возможность гарантировать ограничения в базовых данных.

class WeirdDictionary : IDictionary<Type, IEnumerable>
{
    private readonly Dictionary<Type, IEnumerable> _data =
        new Dictionary<Type, IEnumerable>();

    public void Add<T>(IEnumerable<T> value)
    {
        _data.Add(typeof(T), value);
    }

    public bool TryGet<T>(out IEnumerable<T> value)
    {
        IEnumerable enumerable;
        if (_data.TryGetValue(typeof(T), out enumerable)
        {
            value = (IEnumerable<T>)enumerable;
            return true;
        }

        value = null;
        return false;
    }

    // use explicit implementation to discourage use of this method since
    // the manual type checking is much slower that the generic version above
    void IDictionary<Type, IEnumerable>.Add(Type key, IEnumerable value)
    {
        if (key == null)
            throw new ArgumentNullException("key");
        if (value != null && !typeof(IEnumerable<>).MakeGenericType(key).IsAssignableFrom(value.GetType()))
            throw new ArgumentException(string.Format("'value' does not implement IEnumerable<{0}>", key));

        _data.Add(key, value);
    }
}

Конец 280Z28

Ответ 5

Создайте собственный класс словаря:

public class BaseClassDictionary<T, IEnumerable<T>> : Dictionary<T, IEnumerable<T>>
    where T : BaseClass
{
}

Затем вы можете использовать этот специализированный словарь вместо типа поля:

private BaseClassDictionary<BaseClassDerivedType, IEnumerable<BaseClassDerivedType>> myDictionary;

Ответ 6

Попробуйте следующее:

public class MyCustomDictionary<T>: Dictionary<T, IEnumerable<T>>  { }