C # .NET 4.0 и Generics - PullRequest
       23

C # .NET 4.0 и Generics

3 голосов
/ 10 апреля 2010

Мне было интересно, может ли кто-нибудь сказать мне, возможно ли такое поведение в C # 4.0

У меня есть иерархия объектов, которую я хотел бы строго типизировать. Как то так

class ItemBase {}

class ItemType<T> where T : ItemBase 
{
   T Base { get; set; }
}


class EquipmentBase : ItemBase {}
class EquipmentType : ItemType<EquipmentBase> {}

Что я хочу иметь, чтобы иметь что-то вроде этого

ItemType item = new EquipmentType();

И я хочу item.Base для возврата типа ItemBase. По сути, я хочу знать, достаточно ли он умен для строгой типизации универсального в базовый класс без строгой типизации. Преимущество этого в том, что я могу просто привести ItemType обратно к EquipmentType и снова получить всю типизацию.

Возможно, я думаю, что все это неправильно ...

Ответы [ 3 ]

4 голосов
/ 10 апреля 2010

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

ItemType<object> item = new EquipmentType();

Вы не можете сделать это в C # 4 по следующим причинам:

  1. Общая ковариация работает только с интерфейсами, массивами и типами делегатов, а не с базовыми классами
  2. Ваш класс ItemType использует T в качестве параметра типа in / out, что означает, что он получает T и также возвращает T.

Номер 2 - главная проблема, потому что если бы это было разрешено, то следующий код должен был бы быть скомпилированным, но при этом не работать во время выполнения.

// this will not work
ItemType<object> item = new EquipmentType();
item.Base = new Object(); // this seems ok but clearly isn't allowed

Часто задаваемые вопросы по ковариации и контравариантности

0 голосов
/ 11 апреля 2010

Я не думаю, что новые функции C # 4.0 помогут вам там. Тем не менее, есть способ обойти это, который уже работает с момента появления дженериков: вы создаете абстрактный базовый класс с тем же именем, что и дженерик, и помещаете все члены, которые вам нужны и которые не должны принимать или возвращать аргумент общий тип, например, так:

class ItemBase {
}

abstract class ItemType {
    public ItemBase Base {
        get { return GetItemBase(); }
        set { SetItemBase(value); }
    }

    protected abstract void SetItemBase(ItemBase value);

    protected abstract ItemBase GetItemBase();
}

class ItemType<T> : ItemType where T : ItemBase {
    protected override sealed void SetItemBase(ItemBase value) {
        Base = (T) value;
    }

    protected override sealed ItemBase GetItemBase() {
        return Base;
    }

    public new T Base { get; set; }
}
0 голосов
/ 10 апреля 2010

Нет, поскольку ItemType в том, что касается компилятора, является отдельным типом от ItemType<EquipmentBase> или ItemType<Foo>. Все три рассматриваются как уникальные типы и не могут представлять друг друга.

В ваших объявлениях класса вы объявили его как ItemType<T>, и поэтому ItemType будет неопределенным типом, который не будет компилироваться.

В лучшем случае вы можете использовать объект ItemType<EquipmentBase> для представления EquipmentType или любой другой класс, производный от ItemType<EquipmentBase>, но не ItemType<PersonType>.

...