Мне интересно, является ли динамический семантически эквивалентным объекту при использовании в качестве параметра универсального типа.
Ваша гипотеза полностью верна.
«динамический» как тип - это не что иное, как «объект» в смешной шапке, в шапке с надписью «вместо выполнения статической проверки типа для этого выражения объекта типа, создайте код, который выполняет проверку типа во время выполнения» , Во всех других отношениях динамика - это просто объект, конец истории.
Мне любопытно, почему существует это ограничение, поскольку они различаются при назначении значений переменным или формальным параметрам.
Подумайте об этом с точки зрения компилятора, а затем с точки зрения верификатора IL.
Когда вы присваиваете значение переменной, компилятор в основном говорит: «Мне нужно сгенерировать код, который выполняет неявное преобразование значения такого-то типа в точный тип переменной». Компилятор генерирует код, который делает это, и верификатор IL проверяет его правильность.
То есть компилятор генерирует:
Frob x = (Frob)whatever;
Но ограничивает преобразования неявными преобразованиями, а не явными преобразованиями.
Когда значение является динамическим, компилятор в основном говорит: «Мне нужно сгенерировать код, который запрашивает этот объект во время выполнения, определяет его тип, снова запускает компилятор и выплевывает небольшой кусок IL, который преобразует любой объект к типу этой переменной, запускает этот код и присваивает результат этой переменной. И если что-то из этого не получается, выбросить. "
То есть компилятор генерирует моральный эквивалент:
Frob x = MakeMeAConversionFunctionAtRuntime<Frob>((object)whatever);
Верификатор даже не мигает при этом. Верификатор видит метод, который возвращает Frob. Этот метод может вызвать исключение, если он не может превратить «что угодно» в Frob; в любом случае, ничего кроме Фроба не будет записано в x.
Теперь подумайте о вашей ковариационной ситуации. С точки зрения CLR, нет такой вещи, как «динамический». Везде, где у вас есть аргумент типа, который является «динамическим», компилятор просто генерирует «объект» в качестве аргумента типа. «динамический» - это функция языка C #, а не функция Common Language Runtime. Если ковариация или контравариантность по «объекту» не являются законными, то и по отношению к «динамическим» они также недопустимы. Там нет IL, который компилятор может генерировать, чтобы заставить систему типов CLR работать по-другому.
Это объясняет, почему вы наблюдаете, как происходит преобразование, скажем, List<dynamic>
в List<object>
; Компилятор знает, что они одного типа. В спецификации фактически говорится, что эти два типа имеют между собой преобразование identity ; они идентичны типов.
Имеет ли это смысл? Вы, кажется, очень заинтересованы в принципах дизайна, которые лежат в основе динамики; вместо того, чтобы пытаться вывести их из первых принципов и экспериментов самостоятельно, вы можете избавить себя от беспокойства и прочитать статьи Криса Берроуза в блоге на эту тему . Он сделал большую часть реализации и изрядного количества дизайна функции.