Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么是;“动态”;当用作泛型类型参数时,对于所有类型都不是协变和逆变的?_C#_.net_Generics_Dynamic_C# 4.0 - Fatal编程技术网

C# 为什么是;“动态”;当用作泛型类型参数时,对于所有类型都不是协变和逆变的?

C# 为什么是;“动态”;当用作泛型类型参数时,对于所有类型都不是协变和逆变的?,c#,.net,generics,dynamic,c#-4.0,C#,.net,Generics,Dynamic,C# 4.0,我想知道当用作泛型类型参数时,dynamic是否在语义上等同于object。如果是这样,我很好奇为什么会存在这种限制,因为在为变量或形式参数赋值时,两者是不同的 我已经用C#4.0编写了一个小实验来梳理一些细节。我定义了一些简单的接口和实现: interface ICovariance<out T> { T Method(); } interface IContravariance<in T> { void Method(T argument); } class Co

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

我已经用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) { }
}
然而,这在
动态
的情况下并不令人信服,因为我们无论如何都会失去类型安全性:

dynamic v1 = new Exception();
string  v2 = v1;
换句话说,问题是“为什么
动态
的语义在赋值和协方差/逆方差与泛型之间有所不同?”

对于这个问题:

ICovariance<string> c7 = new Covariance<dynamic>();
它肯定会失败,除非
dynamic
不是
string
或具有这些方法

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

记住这条规则,
D
B
是协变的,如果存在从
D
B
的转换。在这种情况下,没有从
动态
字符串
的强制转换

但是
dynamic
对象是协变的,因为一切都是从它派生出来的。

“为什么赋值和协变/逆变与泛型之间的动态语义不同?”

答案是,当使用泛型时,您是从数据类型本身抽象出来的。然而,它也意味着泛型足够泛型,所有类型都将共享相同的功能

所以如果你有'ICovariance c9=新协方差();`动态和异常都没有相同的功能(作为基本类型)。此外,编译器对于如何从动态转换为异常(即使它们都是从对象继承的)一无所知

如果在
动态
异常
(对象除外)之间有一个明确的继承层次结构,那么这在某种程度上是可以的

原因在某种程度上是因为你可以向下投射,但不能向上投射。例如,若异常继承自dynamic,那个么它就可以了。如果dynamic从Exception继承,这将是一种向上转换的处理,这是不好的,因为可能存在这样的情况,即“dynamic
的数据不存在于
Exception”中


.NET内置了这些显式类型转换,您可以在
System.Convert
对象中看到它们的作用。但是,如果没有自定义代码,超特定的类型就不能很容易地在彼此之间隐式或显式地强制转换。这就是为什么拥有多个类型失败的原因之一(如“ICOVERANCE c9=new convariance();”)。这也是为了保护类型安全而构建的。

因为动态和协变/逆变关键字是如此新

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

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

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

你的猜测完全正确

“动态”作为一种类型,只不过是戴着一顶滑稽帽子的“对象”,帽子上写着“与其对类型对象的这个表达式进行静态类型检查,不如生成在运行时进行类型检查的代码”。在所有其他方面,动态只是对象,故事的结尾

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

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

当你给一个变量赋值时,编译器基本上会说“我需要生成代码,将某个类型的值隐式转换为变量的确切类型”。编译器生成执行此操作的代码,IL验证器验证其正确性

也就是说,编译器生成:

Frob x = (Frob)whatever;
但将转换限制为隐式转换,而不是显式转换

当值是动态的时,编译器基本上会说“我需要生成代码,在运行时查询这个对象,确定它的类型,再次启动编译器,并吐出一小段IL,将这个对象转换为这个变量的类型,运行代码,并将结果分配给这个变量。如果其中任何一个失败了,扔掉。”

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

Frob x = MakeMeAConversionFunctionAtRuntime<Frob>((object)whatever);
Frob x=makemeaconversionfunctiontruntime((对象)随便什么);
验证器甚至不眨眼。验证器看到一个返回Frob的方法。如果该方法无法将“whatever”i
c7.Method().IndexOf(...);
Frob x = (Frob)whatever;
Frob x = MakeMeAConversionFunctionAtRuntime<Frob>((object)whatever);