Обеспечил ли .NET 4 решение этой простой проблемы с общим интерфейсом? - PullRequest
2 голосов
/ 07 сентября 2010

Предположим, у вас есть такой интерфейс:

public interface IDoSomething<out T>
{
    T DoSomething(object value);
}

Чтобы иметь возможность вызвать DoSomething на этом интерфейсе, не зная типа T, вы должны создать это:

public interface IDoSomething
{
    object DoSomething(object value);
}

Измените свой оригинальный интерфейс для наследования от нового:

public interface IDoSomething<out T> : IDoSomething
{
    new T DoSomething(object value);
}

А затем сделайте это:

((IDoSomething)obj).DoSomething();

'' 'Но' '' тогда вы должны реализовать метод DoSomething в своей реализации дважды; один раз явно и один раз неявно.

Одно из решений, которое частично работает, заключается в следующем:

((IDoSomething<object>)obj).DoSomething();

Но это работает, только если универсальный тип является ссылочным типом; Типы значений типа DateTime и int не будут работать, даже если они тоже наследуются от object. Почему это?!

Есть ли решение этой загадки (без использования абстрактных классов!)?

Ответы [ 3 ]

4 голосов
/ 07 сентября 2010

Типы значений скважины не поддерживаются.

Значение и причина, насколько я знаю, главным образом связаны с разницей в хранении массивов.

Давайте представим, что у нас есть:

Byte[] bb = new Byte[20];
byte b = 255;
bb[0] = b; // here value is copied into the array
b = 128;
Console.WriteLine(bb[0]); // will print 255

Таким образом, изменениеb не меняет значение, которое мы присвоили, и это ожидается.Но что если мы сделаем что-то вроде этого:

object[] bb = new Byte[20]; // throws exception

Система обрабатывает массивы объектов по-разному и сохраняет указатель, а не значение.На самом деле у нас есть только один тип массива объектов (с точки зрения структуры, а не типа), и это массив указателей, в то время как массивы для типов значений хранят значение.

3 голосов
/ 07 сентября 2010

В этой статье объясняется, почему дисперсия не работает для типов значений (примерно на 1/4 ниже).По сути, int -> object изменяет представление данных (требуется бокс), где string -> object просто меняет тип указателя.

0 голосов
/ 19 марта 2013

В общеязыковой среде выполнения каждое определение типа значения фактически определяет два типа объектов: тип объекта кучи, который наследуется от System.Object и ведет себя так же, как и тип хранилища, который ведет себя как набор байтов, которые на самом деле ничего не наследует, и его значение полностью зависит от кода, который обращается к нему. Если передать тип значения подпрограмме, которая ожидает ссылку на объект кучи, система создаст новый объект кучи (соответствующего типа объекта кучи), скопирует данные типа значения в этот объект и передаст ссылку на него. С другой стороны, если кто-то вызывает универсальный метод с типом значения в качестве одного из его параметров универсального типа, среда выполнения сгенерирует специальную версию этого метода только для этого конкретного типа значения; рассматриваемый код будет знать, как интерпретировать байты, выделенные для хранения любых переменных этого типа.

Код, который ожидает, что IEnumerable<Object> будет совершенно доволен любым IEnumerable<T>, который возвращает вещи, которые действительно происходят от Object. Однако IEnumerable<Int32> не возвращает экземпляры типа кучи, связанного с Int32 (который происходит от Object), но вместо этого возвращает наборы байтов, которые были бы бессмысленными для любого кода, который не был специально сгенерирован иметь дело со значениями типа Int32.

...