[英]There is a difference between classes and structs as covariant type parameters
如果在接口中用作协变类型参数的类型是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>);
}
为什么结构和类之间有区别 ?
通俗地说,因为将引用类型视为其他类型(祖先或后代)只涉及编译器更新其内部簿记结构; 什么都不需要在运行时更改,因为所有引用类型的内存中表示具有相同的结构(在标准中,这涉及隐式引用转换 )。
另一方面,值类型具有(可能)不同的内存中表示,因此将值类型A的实例视为值类型B的实例必然涉及运行时转换。
因为结构是按值的。 没有装箱操作,你不能将结构“转换”为另一个东西(接口)。
“out”只是关于强制转换:它允许你将IEnumerable<MyClass>
为IEnumerable<MyClassBase>
(你将枚举相同的对象,具有不同的类型,没有成本)......但这对于结构来说根本没有意义(拳击是必需的)。
如果类FooClass
和struct FooStruct
都实现IFoo
,则类型为FooClass
的变量是对IFoo
实现的引用 ,但FooStruct
类型的变量本身就是 IFoo
的实现。 可以使用引用类型进行协方差的原因是,如果T
派生自U
,则对T
每个引用都将是对U
的引用; 如果对T
的引用传递了一个期望引用U
,则接收的参数将是对U
的引用,并且该方法不必关心它也是对T
的引用。
协方差原因不与结构类型的工作是该类型的值Int32
不是对实现一个堆对象的引用IComparable<Int32>
--IT 是的一个实现IComparable<Int32>
具有参数类型IComparable<Int32>
将不会期望接收IComparable<Int32>
- 它将期望接收引用。
注意,有些语言试图假装给出Int32 v1; Object v2 = v1;
的声明Int32 v1; Object v2 = v1;
Int32 v1; Object v2 = v1;
v1
的类型和v2
保存引用的对象的类型是同一个。 实际上,它们是居住在不同宇宙中的不同类型。 只要运行时环境看到派生自System.ValueType
System.Enum
以外的类,它就会在与堆类型分开的存储位置类型的Universe中有效地定义第二种类型。 如果有人说IComprable<Int32> v3 = v1;
,正在做的是要求系统创建一个堆对象类型 Int32
的实例,其内容从v1
加载,并存储到v3
的引用。 虽然系统允许从结构类型到相应的堆对象类型的隐式转换,以及另一种方式的显式转换,但这并不意味着变量和堆对象是相同的类型。 实际上,需要转换的事实意味着它们不是。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.