Мне интересно, является ли 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;
Другими словами, возникает вопрос «Почему семантика динамического
различается между присваиванием и ковариацией / контравариантностью с дженериками?»