繁体   English   中英

类和结构之间存在差异作为协变类型参数

[英]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>); 
}

为什么结构和类之间有区别

这种行为是设计但非常混乱 ,引用官方FAQ

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

通俗地说,因为将引用类型视为其他类型(祖先或后代)只涉及编译器更新其内部簿记结构; 什么都不需要在运行时更改,因为所有引用类型的内存中表示具有相同的结构(在标准中,这涉及隐式引用转换 )。

另一方面,值类型具有(可能)不同的内存中表示,因此将值类型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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM