StackOverflowException使用具有协变泛型参数的explict接口实现

时间:2015-09-15 19:33:36

标签: c# generics inheritance covariance

我使用协变泛型参数扩展了现有的IClonable接口。

public interface ICloneable<out T> : ICloneable
{
    new T Clone();
}

现在我在基类中实现了这个接口。

public class Base : ICloneable<Base>
{
    public string StrValue { get; set; }

    Base ICloneable<Base>.Clone()
    {
        var result = (Base)FormatterServices.GetUninitializedObject(this.GetType());
        result.StrValue = this.StrValue;
        return result;
    }

    public virtual object Clone()
    {
        return ((ICloneable<Base>)this).Clone();
    }
}

调用Clone()按预期工作,并返回具有相同值的新基类实例。

我创建了一个Base的派生类,它再次实现了接口ICloneable<T>以返回这个新类型:

public class Sub : Base, ICloneable<Sub>
{
    public int IntValue { get; set; }

    Sub ICloneable<Sub>.Clone()
    {
        var result = (Sub)base.Clone();
        result.IntValue = this.IntValue;
        return result;
    }

    public override object Clone()
    {
        return ((ICloneable<Sub>)this).Clone();
    }
}

但是如果我在Clone()的实例上调用Sub,我会遇到StackOverflowException,因为object Base.Clone()调用类Sub ICloneable<Sub>.Clone()的{​​{1}}。

问题是协变泛型类型参数。如果我删除Sub所有内容都按预期工作。

问题是为什么out指向((ICloneable<Base>)this).Clone()

共同和逆变量只是语法糖,编译器在派生层次结构中搜索最低可能的类型吗?这意味着编译器将Sub.Clone()更改为ICloneable<Base>

我没有找到解释这种行为的官方理由。

测试代码(不包括界面ICloneable<Sub>Base):

Sub

1 个答案:

答案 0 :(得分:5)

  

共同和逆变只有语法糖,编译器会在派生层次结构中搜索尽可能低的类型吗?

合作/逆转与句法糖无关。它允许您在编译时传递更小的&#34;更小的特定类型(协方差)和更大的(逆变)与特定的编译器限制。

由于您的T参数已标记为out,因此CLR将查看Clone的任何覆盖实施的运行时。

编译时绑定是callvirtbase.Clone,不会发生变化:

.method public hidebysig newslot virtual 
instance object Clone () cil managed 
{
    // Method begins at RVA 0x2089
    // Code size 7 (0x7)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: callvirt instance !0 class ICloneable`1<class Base>::Clone()
    IL_0006: ret

} // end of method Base::Clone

运行时是polymorphism happens

的位置

删除out调用基础的事实强调了这一点。