为什么是“动态的”当用作通用类型参数时,不是所有类型的协方差和相反变量? [英] Why is "dynamic" not covariant and contravariant with respect to all types when used as a generic type parameter?

查看:95
本文介绍了为什么是“动态的”当用作通用类型参数时,不是所有类型的协方差和相反变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道如果 dynamic 在语法上等同于 object ,用作通用类型参数。如果是这样,我很好奇为什么这个限制存在,因为两者在将值分配给变量或形式参数时是不同的。

I am wondering if dynamic is semantically equivalent to object when used as a generic type parameter. If so, I am curious why this limitation exists since the two are different when assigning values to variables or formal parameters.

我在C#4.0中写了一个小实验挑逗一些细节。我定义了一些简单的接口和实现:

I've written a small experiment in C# 4.0 to tease apart some of the details. I defined some simple interfaces and implementations:

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 来显示动态可以以同样的方式用作通用类型参数。

The first two statements with c1 and c2 demonstrate that basic covariance and contravariance are working. I then use c3 and c4 to show that dynamic can be used as a generic type parameter in the same fashion.

c5 c6 显示从 dynamic 对象的转换始终有效。这不是真的太令人惊讶,因为对象是所有其他类型的祖先。

The statements with c5 and c6 reveal that a conversion from dynamic to object is always valid. This isn't really too surprising, since object is an ancestor of all other types.

最后的实验 c7 c8 是我开始困惑的地方。这意味着返回 dynamic 对象的方法不会替代返回 string 的方法,类似于接受的方法 string 对象不能采用动态。最后两个语句与赋值和方法调用显示这显然不是这样,所以我的困惑。

The final experiment with c7 and c8 is where I start to become confused. It implies that methods that return dynamic objects are not substitutes for methods that return string ones, and similarly that methods that accept string objects cannot take dynamic ones. The final two statements with the assignment and method call show this is clearly not the case, hence my confusion.

我想了一下,想知道这是否是阻止程序员使用 ICovariance< dynamic> 作为导致运行时错误的类型转换之间的垫脚石,例如:

I thought about this a little, and wondered if this is to prevent programmers from using ICovariance<dynamic> as a stepping stone between type conversions that would result in run-time errors, such as:

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

然而,在动态因为我们失去类型安全性:

However, this is unconvincing in the case of dynamic since we lose type-safety anyway:

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

换句话说,问题是为什么动态在赋值和协方差/泛型之间不同?

Put another way, the question is "Why does the semantics of dynamic differ between assignment and covariance/contravariance with generics?"

推荐答案


我想知道当用作泛型类型参数时,动态是否在语义上等同于对象。

I am wondering if dynamic is semantically equivalent to object when used as a generic type parameter.

您的猜想是完全正确。

动态作为一种类型只不过是一个有趣的帽子的对象,一个帽子说而不是进行静态类型检查表达类型对象,生成在运行时执行类型检查的代码。在所有其他方面,动态只是对象,故事结束。

"dynamic" as a type is nothing more than "object" with a funny hat on, a hat that says "rather than doing static type checking for this expression of type object, generate code that does the type checking at runtime". In all other respects, dynamic is just object, end of story.


我很好奇为什么这个限制存在,因为两者在分配时是不同的值为变量或形式参数。

I am curious why this limitation exists since the two are different when assigning values to variables or formal parameters.

从编译器的角度来看,然后从IL验证者的角度来考虑它。

Think about it from the compiler's perspective and then from the IL verifier's perspective.

当您将值分配给变量时,编译器基本上会说:我需要生成代码,从一个值和这样的类型到一个类型的确切类型进行隐式转换变量。编译器生成代码,IL验证器验证其正确性。

When you're assigning a value to a variable, the compiler basically says "I need to generate code that does an implicit conversion from a value of such and such a type to the exact type of the variable". The compiler generates code that does that, and the IL verifier verifies its correctness.

编译器生成:

Frob x = (Frob)whatever;

但将转化限制为隐式转化,而不是明确转换。

But limits the conversions to implicit conversions, not explicit conversions.

当值为动态时,编译器基本上说我需要生成在运行时询问这个对象的代码,确定它的类型,再次启动编译器,并且吐出一小部分IL来转换任何内容此对象是该变量的类型,运行该代码,并将结果分配给此变量,如果其中任何一个失败,则抛出。

When the value is dynamic, the compiler basically says "I need to generate code that interrogates this object at runtime, determines its type, starts up the compiler again, and spits out a small chunk of IL that converts whatever this object is to the type of this variable, runs that code, and assigns the result to this variable. And if any of that fails, throw."

也就是说,编译器生成道德上的等价物:

That is, the compiler generates the moral equivalent of:

Frob x = MakeMeAConversionFunctionAtRuntime<Frob>((object)whatever);

验证者甚至不会闪烁。验证者看到一种返回Frob的方法。如果无法将任何变成Frob,那么该方法可能会抛出异常;无论哪种方式,只有Frob才能写入x。

The verifier doesn't even blink at that. The verifier sees a method that returns a Frob. That method might throw an exception if it is unable to turn "whatever" into a Frob; either way, nothing but a Frob ever gets written into x.

现在考虑您的协方差情况。从CLR的角度来看,没有动态的东西。你所有的类型参数都是动态的,编译器只需生成一个对象作为一个参数。 动态是C#语言功能,而不是公共语言运行时功能。如果对象的协方差或相反性是不合法的,那么动态也不合法。编译器不会产生IL,使CLR的类型系统工作不同。

Now think about your covariance situation. From the CLR's perspective, there is no such thing as "dynamic". Everywhere that you have a type argument that is "dynamic", the compiler simply generates "object" as a type argument. "dynamic" is a C# language feature, not a Common Language Runtime feature. If covariance or contravariance on "object" isn't legal, then it isn't legal on "dynamic" either. There's no IL that the compiler can generate to make the CLR's type system work differently.

这就解释了为什么你观察到有一个转换,比如说, 列表< object> 编译器知道它们是相同的类型。该规范实际上称这两种类型之间具有标识转换;他们是相同的类型。

This then explains why it is that you observe that there is a conversion from, say, List<dynamic> to and from List<object>; the compiler knows that they are the same type. The specification actually calls out that these two types have an identity conversion between them; they are identical types.

这一切都有道理吗?你似乎对设计原理感到非常的兴奋,这些设计原则是动态的而不是尝试从自己的第一个原则和实验中推断出它们,您可以节省自己的麻烦,并阅读

Does that all make sense? You seem very interested in the design principles that underly dynamic; rather than trying to deduce them from first principles and experiments yourself, you could save yourself the bother and read Chris Burrows' blog articles on the subject. He did most of the implementation and a fair amount of the design of the feature.

这篇关于为什么是“动态的”当用作通用类型参数时,不是所有类型的协方差和相反变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆