В C #: Как объявить универсальный словарь с типом в качестве ключа и IEnumerable <> этого типа в качестве значения? - PullRequest
24 голосов
/ 31 мая 2010

Я хочу объявить словарь, в котором хранятся напечатанные 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);

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

Ответы [ 6 ]

25 голосов
/ 01 июня 2010

Используйте 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);
15 голосов
/ 31 мая 2010

Возможно, вам даже не понадобится словарь, чтобы это сделать, но это зависит от ваших потребностей. Если вам нужен только 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 проверить это.

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

11 голосов
/ 31 мая 2010

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

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

    public Foo(IDictionary<T, IEnumerable<T>> dataOfType)
    {
        this._dataOfType = dataOfType;
    }   
}   
7 голосов
/ 31 мая 2010
  1. Вы не можете ограничить конкретную переменную. Это работает только на классах и методах. Честно говоря, на уровне переменных это не имеет никакого смысла.
  2. Вам нужен пользовательский класс - class WeirdDictionary : IDictionary<Type, IEnumerable>, который перегрузит метод Add, чтобы получить тип и IEnumerable этого типа, что вы можете сделать с помощью ограничений, и приведете IEnumerable <> к IEnumerable. Перегрузите также индексатор и приведите его к соответствующему типу.
    Весь этот кастинг необходим, так как дженерики строги, так как IEnumerable<Base> так же хорош, как и IEnumerable<Derived> (это называется дисперсией, я полагаю?)

Это решение немного обобщено, так как повторное использование пород

Редактировать с помощью 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

4 голосов
/ 31 мая 2010

Создать пользовательский словарь класса:

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

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

private BaseClassDictionary<BaseClassDerivedType, IEnumerable<BaseClassDerivedType>> myDictionary;
1 голос
/ 31 мая 2010

Попробуйте это:

public class MyCustomDictionary<T>: Dictionary<T, IEnumerable<T>>  { } 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...