首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么当作为泛型类型参数时,对于所有类型来说,“动态”不是协变和反变?

为什么当作为泛型类型参数时,对于所有类型来说,“动态”不是协变和反变?
EN

Stack Overflow用户
提问于 2011-02-03 22:52:27
回答 4查看 1.6K关注 0票数 22

当作为泛型类型参数使用时,我想知道dynamic在语义上是否等同于object。如果是这样的话,我很好奇为什么会存在这种限制,因为两者在为变量或形式参数赋值时是不同的。

我用C# 4.0编写了一个小实验来梳理一些细节。我定义了一些简单的接口和实现:

代码语言:javascript
复制
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) { }
}

实验的有趣细节如下:

代码语言:javascript
复制
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);       
    }
}

使用c1c2的前两个语句显示了基本协方差和反向方差是有效的。然后,我使用c3c4来表明dynamic可以以相同的方式作为泛型类型参数使用。

带有c5c6的语句显示,从dynamicobject的转换总是有效的。这并不奇怪,因为object是所有其他类型的祖先。

c7c8的最后一个实验中,我开始感到困惑。这意味着返回dynamic对象的方法不能代替返回string对象的方法,同样地,接受string对象的方法不能接受dynamic对象。最后两条带有赋值和方法调用的语句表明,情况显然不是这样,因此我感到困惑。

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

代码语言:javascript
复制
ICovariance<dynamic> c9 = new Covariance<Exception>();
ICovariance<string> c10 = c9;
// While this is definitely not allowed:
ICovariance<string> c11 = new Covariance<Exception>();

然而,在dynamic的情况下,这是不可信的,因为我们无论如何都失去了类型安全性:

代码语言:javascript
复制
dynamic v1 = new Exception();
string  v2 = v1;

换句话说,问题是“为什么的语义在赋值和协方差/逆方差与泛型之间存在差异?”

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-02-04 00:37:22

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

你的猜想是完全正确的。

"dynamic“作为类型只不过是带着滑稽帽子的" object”,它写着“而不是对类型对象的表达式执行静态类型检查,而是生成在运行时进行类型检查的代码”。在所有其他方面,动态只是对象,故事的结尾。

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

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

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

也就是说,编译器生成:

代码语言:javascript
复制
Frob x = (Frob)whatever;

但将转换限制为隐式转换,而不是显式转换。

当值是动态的时,编译器基本上会说:“我需要生成代码,在运行时询问这个对象,确定它的类型,再次启动编译器,然后释放出一小块IL,它将这个对象转换成这个变量的类型,运行该代码,并将结果分配给这个变量。如果其中任何一个失败,就抛出。”

也就是说,编译器生成的道德等效于:

代码语言:javascript
复制
Frob x = MakeMeAConversionFunctionAtRuntime<Frob>((object)whatever);

验证者甚至都不眨眼。验证器看到一个返回Frob的方法。如果无法将“任何东西”转换为Frob,则该方法可能会引发异常;无论如何,除了Frob之外,任何东西都不会被写入x。

现在想想你的协方差情况。从CLR的角度来看,没有“动态”这样的东西。只要有一个类型参数是“动态的”,编译器就会生成"object“作为类型参数。"dynamic“是C#语言特性,而不是公共语言运行时特性。如果“对象”上的协方差或反方差不合法,那么它在“动态”上也是不合法的。编译器无法生成IL来使CLR的类型系统以不同的方式工作。

这就解释了为什么您观察到从(比方说) List<dynamic>到和从List<object>的转换;编译器知道它们是相同的类型。实际上,规范要求这两种类型之间进行身份转换;它们是相同的类型。

这一切都有意义吗?您似乎对动态的设计原则非常感兴趣;与其尝试从最初的原则和实验中推断出它们,您还可以省去麻烦,阅读克里斯·布伦斯关于这个问题的博客文章。他完成了大部分的实现和相当多的功能设计。

票数 23
EN

Stack Overflow用户

发布于 2011-02-03 22:59:16

这一次:

代码语言:javascript
复制
ICovariance<string> c7 = new Covariance<dynamic>();

理由很明显,如果有可能的话,你可以:

代码语言:javascript
复制
c7.Method().IndexOf(...);

而且它肯定会失败,除非dynamic不是string或者有这些方法。

因为(即使经过所有的更改) c#不是动态语言。只有在绝对安全的情况下才允许协方差。当然,您可以直接调用IndexOf变量,但是您不能让您的API用户无意中这样做。例如,如果您返回这样一个带有ICovariance<string>dynamic,秘密调用代码可能会失败!

记住,如果有从DB的强制转换,那么B是协变的。在本例中,不存在从dynamicstring的强制转换。

但是,dynamicobject的协变体,因为所有东西都是从它派生出来的。

票数 2
EN

Stack Overflow用户

发布于 2011-02-03 23:09:27

因为动态和协变/反变关键字都是新的?

我猜你回答了你自己的问题。赋值类型安全在赋值语句中是放松的,因为这是动态工作的方式;它是短路编译时类型检查,这样您就可以从编译器不知道的对象中进行分配。

但是,泛型协方差/逆方差是严格控制的;如果不使用in/out关键字(在C# 4.0中与dynamic一起引入),您就无法以任何一种方式进行转换。泛型参数,即使允许协/反向方差,也要求类型位于继承层次结构的同一分支中。字符串不是动态的,动态也不是字符串(虽然两者都是对象,动态可能引用可以作为字符串访问的内容),因此协方差/反向方差检查中固有的泛型类型检查失败,而OTOH则明确要求编译器忽略大多数涉及动态的非泛型操作。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4892704

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档