Невозможно привести MyType <T>к MyType <object> - PullRequest
2 голосов
/ 18 февраля 2012

Конкретное исключение, которое я получаю:

Невозможно привести объект типа NbtByte к типу INbtTag<System.Object>

На этой строке:

tag = (INbtTag<object>)new NbtByte(stream);

Где tag объявлено как:

INbtTag<object> tag;

А NbtByte определяется как:

public class NbtByte : INbtTag<byte>

Где IBtTag:

public interface INbtTag<out T>

Я подумал, объявив это как out T Я мог бы делать такие вещи.

В основном, я хочу иметь словарь IbtTag<T> s,

var dict = new Dictionary<string, INbtTag<object>>();

но где T разных типов (поэтому я объявил это с object). Это возможно?

Ответы [ 2 ]

7 голосов
/ 18 февраля 2012

Дисперсия интерфейса применяется только к ссылочным типам.Типы значений (например, целые числа, байты и т. Д., А также пользовательские структуры) исключаются.Например, вы не можете использовать массив целых чисел как IEnumerable<object>, даже если массив равен IEnumerable<int>.

IEnumerable<object> objs = new int[] { 1, 2, 3 }; // illegal
IEnumerable<object> objs = new string[] { "a", "b", "c" }; // legal

Чтобы решить вашу проблему со словарем, возможно, вы решите определить неуниверсальныйинтерфейс.(Если ваш универсальный интерфейс может представлять членов как тип T, неуниверсальный интерфейс будет просто предоставлять object.)

Скажем, у вас есть

interface INbtTag { } // non-generic interface 
interface INbtTag<out T> : INbtTag { } // covariant generic interface

Тогда вы можете использовать свой словарь какDictionary<string, INbtTag>.

Недостатком является то, что при реализации интерфейса необходимо реализовать , оба. Обычно это означает, что реализация универсальной версии выполняется неявно, а не универсальная - явно.Например:

interface INbtTag
{
    object GetValue(); 
}

interface INbtTag<out T> : INbtTag
{
    T GetValue();
}

class NbtByte : INbtTag<byte>
{
    byte value;

    public byte GetValue() // implicit implementation of generic interface
    {
        return value;
    }

    object INbtTag.GetValue() // explicit implementation of non-generic interface
    {
        return this.GetValue(); // delegates to method above
    }
}
1 голос
/ 18 февраля 2012

Одним из ограничивающих аспектов использования универсальных типов является то, что универсальные типы не так гибки в преобразованиях типов, как традиционные типы.List<Object> не совместимо с присвоением с List<String> или любым другим типом объекта.

В Linq есть вспомогательные функции преобразования, такие как .Cast<T>(), которые будут логически преобразовывать каждый элемент из одного списка в тип T для формирования нового списка типа List<T>.Хотя вспомогательная функция удобна, она не меняет того факта, что List<N> не совместима по типу с List<T>, даже если N и T каким-то образом совместимы по типу.

По сути, универсальныйтипы не являются полиморфными. Не существует общего типа между экземплярами одного и того же универсального типа - ни один тип, с которым вы можете объявить переменную, может содержать List<T> и List<N>.

Если вы создаете свой собственный универсальный тип и хотите иметь какой-то общий тип, который можно использовать для переноса всех проявлений универсального типа, вам нужно выполнить некоторую гимнастику типов, чтобы заставить его работать,и вы будете ограничены в том, что вы можете сделать.Вам нужно определить базовый класс или тип интерфейса, а затем вам нужно сделать так, чтобы ваш универсальный тип наследовал от базового класса или реализовал тип интерфейса.В этой конфигурации вы можете объявить переменную с типом базового класса или типом интерфейса и назначить MyClass<N> и MyClass<T> одной и той же переменной, получая доступ только к членам, определенным в базовом классе (которые, конечно, не будут иметь никакихзнание параметров типа N или T).

Параметры ковариантного типа (IMyInterface<out T>) могут помочь вам в этом, но ковариация накладывает серьезные ограничения на то, что вы можете делать в этом интерфейсе - ковариантный тип Tможет использоваться только для результатов функции, но не для аргументов или настраиваемых свойств.

Простой факт, что IList<T> не объявлен ковариантным, не является случайностью или недосмотром - он не может быть ковариантным.Чтобы быть полезным IList<T> нужны такие методы, как Add (T item) и Remove (T item), которые запрещены, когда T ковариантен.

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