Я хочу объявить словарь, который хранит введенный 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);
Как определить такой словарь?
Возможно, вам даже не понадобится словарь, чтобы иметь возможность делать это - но это зависит от ваших потребностей. Если вам нужен только один такой список для каждого типа на appdomain (т.е. "словарь" является статическим
), то следующий паттерн может быть эффективным и хорошо продвигает вывод типов:
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
, включая IBase
, без ошибок во время выполнения или компиляции - что может быть особенностью или ошибкой, которая только и ждет своего часа, так что вы можете захотеть if
для проверки этого.
Кроме того, этот подход игнорирует потоки; поэтому если вы хотите получить доступ к этой структуре данных из нескольких потоков, вам, вероятно, потребуется добавить блокировку. Чтение/запись ссылок атомарны, так что вы не получите повреждений, если не заблокируете, но неактуальные данные и условия гонки, конечно, возможны.
Создайте собственный класс словаря:
public class BaseClassDictionary<T, IEnumerable<T>> : Dictionary<T, IEnumerable<T>>
where T : BaseClass
{
}
Затем вы можете использовать этот специализированный словарь вместо типа поля:
private BaseClassDictionary<BaseClassDerivedType, IEnumerable<BaseClassDerivedType>> myDictionary;
Вы не ограничиваете T
в частном члене; вы ограничиваете его на уровне класса.
class Foo<T> where T : BaseClass
{
private IDictionary<T, IEnumerable<T>> _dataOfType;
public Foo(IDictionary<T, IEnumerable<T>> dataOfType)
{
this._dataOfType = dataOfType;
}
}
класс WeirdDictionary: IDictionary
, который перегрузит метод Add, чтобы принять Type и IEnumerable этого типа, что можно сделать с помощью ограничений. и приведите IEnumerable <> к IEnumerable. Также перегрузите индексатор и верните его к соответствующему типу. IEnumerable
был так же хорош, как IEnumerable
(Полагаю, это называется дисперсией?) Это решение является немного обобщенным, так как повторное использование камней
Править 280Z28:
Сначала я пометил это, потому что пункт 2 сбивал с толку и неправильно его истолковал. Используя явную реализацию методов в IDictionary
и предоставляя общие альтернативы, вы можете получить довольно чистый интерфейс. Обратите внимание, что вы не можете создавать универсальные индексаторы, поэтому вам всегда придется использовать TryGet
(что в любом случае является хорошей идеей). Я только включил явную реализацию одного из методов IDictionary <>
, чтобы показать, как выполнять проверки. Не создавайте WeirdDictionary
напрямую из Dictionary
, иначе вы потеряете возможность гарантировать ограничения в базовых данных.
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
Попробуйте следующее:
public class MyCustomDictionary<T>: Dictionary<T, IEnumerable<T>> { }
Используйте System.ComponentModel.Design.ServiceContainer
, который уже доступен в платформе .Net.
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);