Можно ли объявить универсальную коллекцию для хранения только объектов, реализующих универсальный интерфейс, инстанцированный с any T
?
Краткий ответ: нет.
Более длинный ответ: нет, потому что это бесполезно.
Давайте рассмотрим простой универсальный интерфейс:
interface I<T> { T Get(); }
И несколько объектов, которые его реализуют:
class Lion : I<Lion>
{
public Lion Get() => this;
}
class TaxPolicyFactory : I<TaxPolicy>
{
public TaxPolicy Get() => new TaxPolicy();
}
class Door: I<Doorknob>
{
public Doorknob Get() => this.doorknob;
...
}
Хорошо, теперь предположим, что у вас есть List<I<ANYTHING>>
, как вы хотите:
var list = new List<I<???>> { new TaxPolicyFactory(), new Lion(), new Door() };
У вас есть список с фабрикой налоговой политики, львом и дверью.Эти типы не имеют ничего общего друг с другом;нет никакой операции, которую вы можете выполнить с каждым из этих объектов.Даже если бы вы могли назвать Get
для каждого из них, у вас была бы последовательность с налоговой политикой, львом и дверной ручкой, и что вы собираетесь с этим делать?
Ничего, это то что.Ограничение "реализует интерфейс I<T>
для любого T
" просто не является полезным ограничением в C #, поэтому нет способа выразить его.
Похоже, у вас проблема "XY".Это проблема, когда у вас плохое решение, и теперь вы задаете вопросы о своем плохом решении. Задайте нам вопрос о вашей реальной проблеме, а не о плохой идее, которую вы получили для ее решения .В чем реальная проблема?
ОБНОВЛЕНИЕ: С новой информацией в вопросе теперь стало намного яснее.Функция, которую вы хотите, называется универсальная ковариация интерфейса , которая была моей любимой функцией для C # 4.
Если вы обновите определение интерфейса до
interface ISyncInterface<out T> { ... }
, тогда вы можетеиспользуйте ISyncInterface<String>
в контексте, где ожидается ISyncInterface<Object>
.Например, вы можете поместить ISyncInterface<Giraffe>
в List<ISyncInterface<Animal>>
или что-либо еще.
Однако вы должны убедиться, что ваше определение интерфейса использует только 1050 * вковариантно действительная позиция .Ваш интерфейс действителен, как указано , но если, например, вы когда-нибудь захотите добавить метод void M(T t);
в ваш интерфейс, он больше не будет ковариантно действительным.«Out» - это мнемоника, говорящая вам, что T
может использоваться только как выход методов.Поскольку IEnumerable<T>
также ковариантно допустимо, это нормально;в IEnumerable<T>
нет входных данных из T
. Кроме того, дисперсия работает только с универсальными интерфейсами и делегатами и различных типовдолжны быть ссылочными типами .Вы не можете поместить ISyncInterface<int>
в List<ISyncInterface<Object>>
, потому что int
не является ссылочным типом.
На SO много сообщений о ковариации и контравариантности;Вы также должны прочитать документацию Microsoft.Это может быть запутанной особенностью.Если вам интересны исторические подробности того, как мы разработали и реализовали эту функцию, см. Мой блог.