c # 4.0: int реальный подтип объекта?ковариация, перечислимые и типы значений - PullRequest
9 голосов
/ 24 марта 2011

Интересно, почему IEnumerable<int> нельзя присвоить IEnumerable<object>. Ведь IEnumerable - это один из немногих интерфейсов, который поддерживает ковариацию ...

  • Отношения подтипов и ковариация работают с ссылочными типами
  • int представляется правильным подтипом object

Однако комбинация обеих функций не работает ...

class A
{
}

class B : A
{
}

class Program
{
    static void Main(string[] args)
    {
        bool b;
        b = typeof(IEnumerable<A>).IsAssignableFrom(typeof(List<B>));
        Console.WriteLine("ienumerable of ref types is covariant: " + b); //true

        b = typeof(IEnumerable<object>).IsAssignableFrom(typeof(List<int>));
        Console.WriteLine("ienumerable of value tpyes is covariant: " + b); //false

        b = typeof(object).IsAssignableFrom(typeof(int));
        Console.WriteLine("int is a subtype of object: " + b); //true
    }
}

спасибо за вашу помощь! Себастиан

Ответы [ 4 ]

8 голосов
/ 24 марта 2011

Типы значений не являются LSP-подтипами объекта до тех пор, пока они не упакованы.

Дисперсия не работает с типами значений.Вообще.


Демонстрация того, что int не является надлежащим подтипом (подтип в смысле LSP) object:

Работает:

object x = new object();
lock (x) { ... }

Не работает (нарушена замещаемость):

int y = new int();
lock (y) { ... }

Возвращает true:

object x = new object();
object a = x;
object b = x;
return ReferenceEquals(a, b);

Возвращает false (заменяемость нарушена):

int y = new int();
object a = y;
object b = y;
return ReferenceEquals(a, b);

Конечно, тема вопроса (разница в интерфейсе) является третьей демонстрацией.

6 голосов
/ 24 марта 2011

Упрощенный ответ заключается в том, что это лишь одна из особенностей способа реализации дисперсии в C # и CLR.

From "Ковариантность и контравариантность в обобщениях" :

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

6 голосов
/ 24 марта 2011

Проблема в том, что object является ссылочным типом, а не типом значения. Единственной причиной, по которой вы можете присвоить int переменной типа object, является бокс.

Чтобы назначить List<int> на IEnumerable<object>, вам нужно поставить в квадрат каждый элемент списка. Вы не можете сделать это, просто назначив ссылку на список и назвав ее другим типом.

1 голос
/ 24 марта 2011

Каждый тип значения в .net имеет соответствующий (в штучной упаковке) тип объекта. Неблокированные типы значений находятся за пределами иерархии типов объектов, но компилятор выполнит расширение от типа значения до типа класса в штучной упаковке. Было бы полезно иметь «класс» в штучной упаковке , который бы поддерживал расширяющиеся преобразования в и из T, но который был бы типом класса. Внутренне, я думаю, что это то, что компилятор делает неявно, но я не знаю способа сделать это явно. Для любого конкретного типа, такого как «целое число», не составит труда определить класс, который будет вести себя как в штучной упаковке , но я не знаю никакого способа сделать такую ​​вещь в общем виде.

...