C # Кастинг дженериков (ковариация и контравариантность?) - PullRequest
9 голосов
/ 06 декабря 2011

Мне нужен какой-то совет / помощь по этому вопросу, я больше не вижу дрова с деревьев.

Это простая серия классов, реализующих некоторые интерфейсы с использованием обобщений.

Тогда я пытаюсь привести конкретные типы, например:

MyGenericObject<SomeObject> _obj;

IMyGenericObject<ISomeObject> _genObj = (IMyGenericObject<ISomeObject>)_obj;

// Неверное приведение

Я читал некоторые статьи о ковариации и контравариантности, но не слишком ясно, почему это невозможно, или как обойти это?


Итак, в этом примере:

public interface IMyObject<in T> where T : IBaseObject
{
    T Activity { get; set; }
}

не сработает ...


.... потому что вы не можете получить и установить свойство Activity.

В этом примере мне нужно было сделать:

public interface IMyObject<out T> where T : IBaseObject
    {
        T Activity { get; }
    }

надеюсь, что это кому-то поможет, и всем спасибо за помощь!

Ответы [ 2 ]

11 голосов
/ 06 декабря 2011

Это можно сделать только в том случае, если вы объявили интерфейс как имеющий ковариантный (out) параметр. Это можно сделать только в том случае, если параметр используется ковариантно.

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

EDIT

В ответ на ваш комментарий к ответу SLaks я испытываю желание повторить все, что Эрик Липперт когда-либо писал о совместной и контрвариантности. См. http://blogs.msdn.com/b/ericlippert/archive/tags/Covariance+and+Contravariance/, а также его ответы в SO (совсем недавно https://stackoverflow.com/a/8380213/385844)

Подведем итог:

Вы не можете разыграть IList<string> на IList<object>, потому что законно передавать FileInfo на IList<object>, но недопустимо передавать его на IList<string>.

Вы не можете привести IList<object> к IList<string>, потому что законно извлечь элемент из IList<string> и назначить его для ссылки на строку, но IList<object> может содержать FileInfo, который может не может быть привязан к строковой ссылке.

РЕДАКТИРОВАТЬ 2

Поскольку вы обратились за советом, также возможно разделить ваши интерфейсы на ко-и контравариантные части. Чтобы продолжить с примером списка, вы могли бы иметь эти интерфейсы

public interface ICovariantList<out T>
{
    T this[int index] { get; }
    //...
}

public interface IContravariantList<in T>
{
    T this[int index] { set; }
    void Add(T item);
    //...
}

public class SomeList<T> : ICovariantList<T>, IContravariantList<T>
{
    //...
}

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

5 голосов
/ 06 декабря 2011

Вы должны объявить интерфейс как имеющий универсальный ковариантный (out) параметр.

...