Почему мой производный класс нельзя преобразовать в базовый класс?

У меня есть следующие классы:

public class Item
{
}

public class ItemCollection<TItem> : ICollection<TItem> where TItem : Item, new()
{
}

У меня есть два производных класса:

public class Feature : Item
{
}

public class Features : ItemCollection<Feature>
{
}

У меня есть класс менеджера для управления такими коллекциями:

public class ItemCollectionManager<TCollection> where TCollection : ItemCollection<Item>, new()
{
}

Я пытался использовать этот класс:

public class FeatureManager : ItemCollectionManager<Features>
{
}

но это приводит к:

«Тип Features должен быть конвертируемым в ItemCollection<Item>, чтобы использовать его как TCollection в универсальном классе ItemCollectionManager<TCollection>».

И как упоминалось ранее,

Features is-a ItemCollection<Feature>

а также

Feature is-a Item.

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

Если бы кто-нибудь мог посоветовать, что я делаю неправильно, это было бы очень признательно.

Спасибо.


person Edward Mulraney    schedule 02.04.2013    source источник


Ответы (2)


Вы не можете сделать...

ItemCollection<Feature> features = ...;
ItemCollection<Item> items = features;

Речь идет о дженериках variance (то есть ковариантности и контравариантности) - и они поддерживаются только для интерфейсов, делегатов - и только при условии, что они спроектированы таким образом (украшены in / out - и соответствуют правилам, которые смирись с этим). например IEnumerable<> (если вы посмотрите его определение, вы увидите out). Для получения более подробной информации, я думаю, лучше всего читать дальше...

Как общая ковариация & Contra-variance реализовано в C# 4.0?
Понимание ковариантных и контравариантных интерфейсов в C#
http://msdn.microsoft.com/en-us/library/dd799517.aspx

В вашем случае вы можете спроектировать IItemCollection<out T> (interface) - это (теоретически) может поддерживать необходимое вам приведение. Но это должно быть read-only (немного упрощенно и не совсем правильно, но так проще думать - правила немного сложнее).

Поскольку вы называете это «коллекцией», я предполагаю, что это не только для перечисления, просмотра элементов, т.е. если у вас есть Add типа (для которого требуется input parameters, т.е. contra-variance), который конфликтует с «ковариацией», которая вам нужна для upcasting. Кроме того, любые другие задействованные параметры могут не позволить сделать ваш интерфейс ковариантным.


Также некоторые другие сообщения, которые я сделал по теме...

Как создать универсальный класс, содержащий набор только своего типа или подтипов в качестве потомков?

Общие обработчики C#, что я неправильно понимаю?

person NSGaga-mostly-inactive    schedule 02.04.2013
comment
+1 за ковариацию и ссылки. И я понятия не имел, что аргументы типа можно украшать входом/выходом. - person Malcolm O'Hare; 02.04.2013
comment
@MalcolmO'Hare, пожалуйста - да, это может быть полезно в определенных сценариях. - person NSGaga-mostly-inactive; 03.04.2013

Вам нужен дополнительный общий аргумент для ItemCollectionManager, TItem.

Я считаю, что ваше определение класса ItemCollectionManager должно выглядеть примерно так.

    public class ItemCollectionManager<TCollection, TItem> where TCollection : ItemCollection<TItem>, new(), 
where TItem : Item
    {
    }

То, как вы определили класс FeatureManager прямо сейчас, было бы вполне приемлемо добавить ЛЮБОЙ КЛАСС, который наследует Item, к TCollection, поскольку единственным ограничением для TCollection является то, что он содержит классы типа Item. Коллекция Features принимает только Feature классы или классы, которые наследуют тип Feature. Поскольку вы не можете добавить базовый тип Item к Features, он не должен компилироваться.

person Malcolm O'Hare    schedule 02.04.2013
comment
Реализация таким образом создает дополнительные проблемы в будущем, и я не считал, что предоставление TItem необходимо, поскольку в любом случае это будет Item. Но если это необходимо, пожалуйста, не могли бы вы объяснить немного больше о том, почему? - person Edward Mulraney; 02.04.2013
comment
Я добавил некоторые пояснения. Если это все еще не ясно, дайте мне знать, и я постараюсь объяснить это лучше. - person Malcolm O'Hare; 02.04.2013