Почему этот бросок невозможен? - PullRequest
4 голосов
/ 07 мая 2009
interface IFolderOrItem<TFolderOrItem> where TFolderOrItem : FolderOrItem {}

abstract class FolderOrItem {}

class Folder : FolderOrItem {}

abstract class Item : FolderOrItem {}

class Document : Item {}

Теперь я пытаюсь сделать что-то вроде этого:

class Something
{
    IFolderItemOrItem<Item> SelectedItem { get; set; }
    void SomeMagicMethod()
    {
        this.SelectedItem = (IFolderOrItem<Item>)GetMagicDocument();
        // bad bad bad ... ??
    }
    IFolderOrItem<Document> GetMagicDocument()
    {
        return someMagicDocument; // which is of type IFolderOrItem<Document>
    }
}

есть ли возможность заставить это работать?

Ответы [ 4 ]

13 голосов
/ 07 мая 2009

Если я правильно прочитал ... тогда проблема в том, что только потому, что Foo : Bar, это не означает, что ISomething<Foo> : ISomething<Bar> ...

В некоторых случаях вариант в C # 4.0 может быть вариантом. В качестве альтернативы, иногда есть вещи, которые вы можете сделать с помощью универсальных методов (хотя я не уверен, что это поможет).


Самое близкое, что вы можете сделать в C # 3.0 (и ниже), это, вероятно, неуниверсальный базовый интерфейс:

interface IFolderOrItem {}
interface IFolderOrItem<TFolderOrItem> : IFolderOrItem
    where TFolderOrItem : FolderOrItem { }

обычно базовый интерфейс будет иметь, например, Type ItemType {get;} для обозначения рассматриваемого реального типа. Тогда использование:

IFolderOrItem SelectedItem { get; set; }
...
public void SomeMagicMethod()
{
    this.SelectedItem = GetMagicDocument(); // no cast needed
    // not **so** bad
}

Из спецификации это относится к §25.5.6 (ECMA 334 v4):

25.5.6 Преобразования

Созданные типы следуют тем же правилам преобразования (§13) как и неуниверсальные типы. При подаче заявления эти правила, базовые классы и интерфейсы сконструированных типов должны быть определенным как описано в §25.5.3.

Никаких специальных преобразований между построенные ссылочные типы, кроме те, которые описаны в §13. Особенно, в отличие от типов массивов, построенных ссылочные типы не разрешают ко-вариантные преобразования (§19.5). это означает, что тип List<B> не имеет преобразование (неявное или явно) до List<A>, даже если B получено из A. Точно нет существует преобразование из List<B> в List<object>.

[Примечание: обоснование это просто: если преобразование в List<A> разрешено, то, видимо, можно хранить значения типа A в список. Однако это сломало бы инвариант, что каждый объект в список типа List<B> всегда является значением типа B, или же неожиданные сбои может произойти при назначении в Коллекционные занятия. конечная нота]

То же самое относится и к интерфейсам. Это меняет бит в C # 4.0, но только в некоторых случаях.

6 голосов
/ 07 мая 2009

Что касается компилятора, IFolderOrItem<Document> & IFolderOrItem<Item> - это два совершенно разных типа.

Document может наследовать Item, но IFolderOrItem<Document> не наследует IFolderOrItem<Item>

Я полагаюсь на Марка или Джона в размещении ссылок на соответствующие части спецификации C #.

3 голосов
/ 07 мая 2009

Проблема в том, что приведение не работает с общими аргументами, а с классом в целом. Документ наследуется от Item, true, но IFolderOrItem не наследуется от IFolderOrItem и никак не связан с ним.

2 голосов
/ 07 мая 2009

Пример, чтобы понять, почему это работает так:

Предположим, IFolderOrItem предоставляет метод, например, void Add (T element).

Ваша реализация для IFolderOrItem предполагает, что параметр является Документом.

Но если вы приведете свой IFolderOrItem к IFolderItemOrItem, тогда кто-нибудь может вызвать метод Create (T), где T должен быть Item.

Приведение из предмета в документ недопустимо, поскольку предмет не является документом.

Единственный способ сделать это - создать неуниверсальную версию интерфейса, позволяющую объектам в качестве параметров проверять тип объекта в ваших реализациях.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...