C# 类和结构作为协变类型参数存在差异

C# 类和结构作为协变类型参数存在差异,c#,generics,covariance,C#,Generics,Covariance,如果在接口中用作协变类型参数的类型是结构,则以下测试失败(在最后一次断言中),但如果是类,则成功 接口IOuter{} 接口IOuter:IOuter{T值{get;} 接口IInner{} 结构内部:IInner{} 类外部:IOuter{public internal Value{get{return new internal();}}} [TestMethod()] 公共无效抵销测试() { var a=新的外部(); Assert.IsTrue(a在外部); //如果内部是结构,则在此失

如果在接口中用作协变类型参数的类型是
结构
,则以下测试失败(在最后一次断言中),但如果是
,则成功

接口IOuter{}
接口IOuter:IOuter{T值{get;}
接口IInner{}
结构内部:IInner{}
类外部:IOuter{public internal Value{get{return new internal();}}}
[TestMethod()]
公共无效抵销测试()
{
var a=新的外部();
Assert.IsTrue(a在外部);
//如果内部是结构,则在此失败。如果内部是类,则成功。
Assert.IsTrue(a在外部);
}
为什么结构和类之间有区别?

引用以下内容,这种行为是:

仅当类型参数是引用类型时,才支持差异


因为结构是按值计算的。 在没有装箱操作的情况下,不能将结构“强制”到另一个对象(接口)


“out”只是关于强制转换:它允许您将
IEnumerable
强制转换为
IEnumerable
(您将使用不同的类型枚举相同的对象,而不需要任何成本)。。。但这对结构来说毫无意义(需要装箱)。

用外行的话说,因为将引用类型视为其他类型(祖先或后代)只需要编译器更新其内部簿记结构;因为所有引用类型的内存中表示具有相同的结构(在standardese中,这涉及隐式引用转换),所以在运行时无需更改任何内容


另一方面,值类型具有(可能)不同的内存表示,因此将值类型A的实例视为值类型B的实例必然涉及运行时转换。

If class
FooClass
和struct
FooStruct
都实现了
IFoo
,类型为
FooClass
的变量是对
IFoo
实现的引用,但类型为
FooStruct
的变量本身是
IFoo
的实现。参考类型可能存在协方差的原因是,如果
T
源自
U
,则对
T
的每个参考都将是对a
U
的参考;如果一个对
T
的引用被传递给了一个希望引用
U
的方法,那么接收到的参数将是对
U
的引用,并且该方法不必关心它也是对
T
的引用

协方差不适用于结构类型的原因是
Int32
类型的值不是对实现
IComparable
的堆对象的引用——它是
IComparable
的实现。参数类型为
IComparable
的方法不希望收到
IComparable
的实现——它希望收到一个引用


注意,一些语言试图假装给定声明
int32v1;对象v2=v1
v1的类型和v2所引用对象的类型是相同的。事实上,它们是居住在不同宇宙中的不同类型。每当运行时环境看到从
System.ValueType
派生的
System.Enum
以外的类时,它都会在存储位置类型(与堆类型分开)的宇宙中有效地定义第二种类型。如果说
icomparable v3=v1,我们正在做的是要求系统创建一个堆对象类型
Int32
的实例,其内容从
v1
加载,并将对该对象的引用存储到
v3
中。尽管系统允许从结构类型隐式转换到相应的堆对象类型,并允许以另一种方式显式转换,但这并不意味着变量和堆对象是同一类型。事实上,需要转换这一事实意味着它们不需要转换。

常见问题解答清楚地表明两者之间存在差异,但它实际上并没有回答您提出的问题:为什么?所以问题是,当使用结构类型参数时,当CLR被强制转换为基参数类型时,它需要将嵌套在泛型接口结果中的所有值装箱?因此,对于它来说,编译器给出错误是有意义的,除非类型参数被强制为引用类型(声明
,其中T:class
),重新考虑后,如果声明它是一个
结构
(比该问题中提出的错误更严重),则这种错误根本不允许将类型与值类型一起使用,而我们只是想防止它们与值类型一起协变使用。@sinelaw:给定一个类似于
Foo的声明,其中T:U
,如果
U
是任何密封类型(包括结构类型),则意味着
T
必须是同一类型。如果
U
是另一种类型,那么
T
可能会有所不同,但如果
U
是一种密封类型或结构,则被困在
U
中,这一事实并不意味着不能为U使用结构。这仅仅意味着
Foo
U
是一种结构时可能没有类时那么通用。同意,正如您所暗示的,主要的一点是在您访问这些项时,隐式地转换这些项。