C # - как создать унаследованную универсальную коллекцию из фабричного метода

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

abstract class ItemBase { }

abstract class CollectionBase<T> : Collection<T> where T : ItemBase, new() { }

... и их производные классы ...

class Item : ItemBase { }

class ItemCollection : CollectionBase<Item> {}

Теперь мне нужен фабричный метод, который создаст ItemCollection. Но обратите внимание, что производные классы Item и ItemCollection неизвестны классу, содержащему этот заводской метод. Вот как я себе это представляю ...

static T CreateItemCollection<T>() where T : CollectionBase<ItemBase>, new()
{
    return new T();
}

... и я представляю, как вызвать его таким образом ...

var collection = CreateItemCollection<ItemCollection>();

Но фабричный метод не будет компилироваться, потому что ItemBase должен иметь конструктор без параметров. И вызов вызова отказывается верить, что ItemCollection является производным от CollectionBase<ItemBase>.

Может кто-нибудь указать мне правильное направление? Спасибо.


person Tim Coulter    schedule 08.09.2009    source источник


Ответы (2)


ItemCollection не является производным от CollectionBase<ItemBase> из-за общей инвариантности. В конце концов, вы можете добавить ItemBase к CollectionBase<ItemBase> - но вы не хотите этого для вашего ItemCollection!

Вам нужно сделать метод универсальным в двух параметрах типа:

static T CreateItemCollection<TCollection, TItem>()
    where TCollection : CollectionBase<TItem>, new()
    where TItem : ItemBase
{
    return new TCollection();
}

Конструктор без параметров требует только тип коллекции. Вы бы назвали это с помощью:

var collection = CreateItemCollection<ItemCollection, Item>();
person Jon Skeet    schedule 08.09.2009
comment
Спасибо. Это решает мою проблему, даже если я все еще не могу полностью понять, почему компилятор настаивает на том, чтобы быть таким строгим (как прокомментировано JaredPar ниже). - person Tim Coulter; 08.09.2009
comment
@ Тим: Как я уже сказал, потому что ItemCollection не должен разрешать все те же вызовы, что и CollectionBase<ItemBase>. Прочтите серию блогов Эрика Липперта о дисперсии для получения более подробной информации - к сожалению, мне нужно запустить сейчас, поэтому у меня нет времени искать ссылку. - person Jon Skeet; 08.09.2009

Проблема здесь в том, что общие ограничения в C # 3.0 имеют некоторую свободу действий в отношении дисперсии. Вместо этого соответствие довольно строгое. Поскольку ItemCollection происходит от CollectionBase<Item>, он не считается производным от CollectionBase<ItemBase>, хотя типы могут казаться совместимыми.

person JaredPar    schedule 08.09.2009