Почему «динамический» нековариантен и контравариантен по отношению ко всем типам при использовании в качестве параметра универсального типа?

Мне интересно, является ли dynamic семантически эквивалентным объекту при использовании в качестве параметр универсального типа. Если так, то мне любопытно, почему существует это ограничение, поскольку они отличаются при присвоении значений переменным или формальным параметрам.

Я написал небольшой эксперимент на C # 4.0, чтобы разобрать некоторые детали. Я определил несколько простых интерфейсов и реализаций:

interface ICovariance<out T> { T Method(); }

interface IContravariance<in T> { void Method(T argument); }

class Covariance<T> : ICovariance<T>
{
    public T Method() { return default(T); }
}

class Contravariance<T> : IContravariance<T>
{
    public void Method(T argument) { }
}

Интересные детали эксперимента:

class Variance
{
    static void Example()
    {
        ICovariance<object> c1 = new Covariance<string>();
        IContravariance<string> c2 = new Contravariance<object>();

        ICovariance<dynamic> c3 = new Covariance<string>();
        IContravariance<string> c4 = new Contravariance<dynamic>();

        ICovariance<object> c5 = new Covariance<dynamic>();
        IContravariance<dynamic> c6 = new Contravariance<object>();

        // The following statements do not compile.
        //ICovariance<string> c7 = new Covariance<dynamic>();
        //IContravariance<dynamic> c8 = new Contravariance<string>();

        // However, these do.
        string s = new Covariance<dynamic>().Method();
        new Contravariance<string>().Method((dynamic)s);       
    }
}

Первые два утверждения с c1 и c2 демонстрируют, что базовая ковариация и контравариантность работают. Затем я использую c3 и c4 , чтобы показать, что dynamic может использоваться как параметр универсального типа таким же образом.

Операторы с c5 и c6 показывают, что преобразование из динамического в объект всегда допустимо. В этом нет ничего удивительного, поскольку объект является предком всех других типов.

Последний эксперимент с c7 и c8 - это то, с чего я начинаю запутаться. Это означает, что методы, возвращающие динамических объектов, не заменяют методы, возвращающие строковые объекты, и аналогично, что методы, которые принимают строковые объекты, не могут принимать динамические объекты. Последние два оператора с присваиванием и вызовом метода показывают, что это явно не тот случай, отсюда и мое замешательство.

Я немного подумал об этом и задумался, чтобы помешать программистам использовать ICovariance как ступеньку между преобразованиями типов, которые могут привести к ошибкам времени выполнения, например:

ICovariance<dynamic> c9 = new Covariance<Exception>();
ICovariance<string> c10 = c9;
// While this is definitely not allowed:
ICovariance<string> c11 = new Covariance<Exception>();

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

dynamic v1 = new Exception();
string  v2 = v1;

Другими словами, возникает вопрос «Почему семантика динамического различается между присваиванием и ковариацией / контравариантностью с дженериками?»

22
задан ide 3 February 2011 в 22:52
поделиться