[英]There is a difference between classes and structs as covariant type parameters
The following test fails (on the last Assert) if the type used as covariant a type parameter in an interface is a struct
, but succeeds if it's a class
. 如果在接口中用作协变类型参数的类型是
struct
,则以下测试失败(在最后一个Assert上),但如果它是class
,则成功。
interface IOuter { }
interface IOuter<out T> : IOuter { T Value { get; } }
interface IInner { }
struct Inner : IInner { }
class Outer : IOuter<Inner> { public Inner Value { get { return new Inner(); } } }
[TestMethod()]
public void ContravarianceTest()
{
var a = new Outer();
Assert.IsTrue(a is IOuter<Inner>);
// Fails here if Inner is a struct. Succeeds if Inner is a class.
Assert.IsTrue(a is IOuter<IInner>);
}
Why is there a difference between structs and classes ? 为什么结构和类之间有区别 ?
This behavior is by design but extremely confusing , to quote the official FAQ : 这种行为是设计但非常混乱 ,引用官方FAQ :
Variance is supported only if a type parameter is a reference type.
仅当类型参数是引用类型时才支持差异。
In layman's terms, because treating a reference type as some other type (either ancestor or descendant) involves nothing more than the compiler updating its internal bookkeeping structures; 通俗地说,因为将引用类型视为其他类型(祖先或后代)只涉及编译器更新其内部簿记结构; nothing at all needs to change at runtime because the in-memory representation of all reference types has the same structure (in standardese this involves an implicit reference conversion ).
什么都不需要在运行时更改,因为所有引用类型的内存中表示具有相同的结构(在标准中,这涉及隐式引用转换 )。
On the other hand, value types have (potentially) distinct in-memory representation, so treating an instance of value type A as an instance of value type B necessarily involves runtime conversions. 另一方面,值类型具有(可能)不同的内存中表示,因此将值类型A的实例视为值类型B的实例必然涉及运行时转换。
Because a struct is by-value. 因为结构是按值的。 You cannot "cast" a struct to another thing (interface) without a boxing operation.
没有装箱操作,你不能将结构“转换”为另一个东西(接口)。
"out" is just about casting: It allows you to cast IEnumerable<MyClass>
to IEnumerable<MyClassBase>
(you will enumerate the same objects, with different type, with no cost) ... but that doesnt makes sense at all for structures (boxing is required). “out”只是关于强制转换:它允许你将
IEnumerable<MyClass>
为IEnumerable<MyClassBase>
(你将枚举相同的对象,具有不同的类型,没有成本)......但这对于结构来说根本没有意义(拳击是必需的)。
If class FooClass
and struct FooStruct
both implement IFoo
, a variable of type FooClass
is a reference to an implementation of IFoo
, but a variable of type FooStruct
is, itself , an implementation of IFoo
. 如果类
FooClass
和struct FooStruct
都实现IFoo
,则类型为FooClass
的变量是对IFoo
实现的引用 ,但FooStruct
类型的变量本身就是 IFoo
的实现。 The reason that covariance is possible with reference types is that if T
derives from U
, every reference to T
will be a reference to a U
; 可以使用引用类型进行协方差的原因是,如果
T
派生自U
,则对T
每个引用都将是对U
的引用; if a reference to a T
is passed a method that expects a reference to a U
, the received parameter will be a reference to U
and the method need not care that it's also a reference to T
. 如果对
T
的引用传递了一个期望引用U
,则接收的参数将是对U
的引用,并且该方法不必关心它也是对T
的引用。
The reason covariance doesn't work with structure types is that a value of type Int32
isn't a reference to a heap object that implements IComparable<Int32>
--it is an implementation of IComparable<Int32>
. 协方差原因不与结构类型的工作是该类型的值
Int32
不是对实现一个堆对象的引用IComparable<Int32>
--IT 是的一个实现IComparable<Int32>
A method with a parameter type IComparable<Int32>
won't be expecting to receive an implementation of IComparable<Int32>
--it will be expecting to receive a reference. 具有参数类型
IComparable<Int32>
将不会期望接收IComparable<Int32>
- 它将期望接收引用。
Note that some languages try to pretend that given the declarations Int32 v1; Object v2 = v1;
注意,有些语言试图假装给出
Int32 v1; Object v2 = v1;
的声明Int32 v1; Object v2 = v1;
Int32 v1; Object v2 = v1;
the type of v1
and the type of the object to which v2
holds a reference are one and the same. v1
的类型和v2
保存引用的对象的类型是同一个。 In reality, they're different types which inhabit different universes. 实际上,它们是居住在不同宇宙中的不同类型。 Any time the runtime environment sees a class other than
System.Enum
derived from System.ValueType
, it effectively defines a second type, in a universe of storage-location types which are separate from the heap types. 只要运行时环境看到派生自
System.ValueType
System.Enum
以外的类,它就会在与堆类型分开的存储位置类型的Universe中有效地定义第二种类型。 If one says IComprable<Int32> v3 = v1;
如果有人说
IComprable<Int32> v3 = v1;
, what one is doing is asking the system to create an instance of heap object type Int32
whose contents are loaded from v1
, and store into v3
a reference to that. ,正在做的是要求系统创建一个堆对象类型
Int32
的实例,其内容从v1
加载,并存储到v3
的引用。 Although the system allows implicit conversions from structure types to the corresponding heap-object types, and explicit conversions the other way, that doesn't mean the variables and heap objects are the same type. 虽然系统允许从结构类型到相应的堆对象类型的隐式转换,以及另一种方式的显式转换,但这并不意味着变量和堆对象是相同的类型。 Indeed, the fact that conversion is needed implies that they are not.
实际上,需要转换的事实意味着它们不是。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.