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

Seb*_*ann 3 c# generics inheritance covariance

我正在IClonable使用协变泛型参数扩展现有接口.

public interface ICloneable<out T> : ICloneable
{
    new T Clone();
}
Run Code Online (Sandbox Code Playgroud)

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

我创建了一个派生类,BaseICloneable<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();
    }
}
Run Code Online (Sandbox Code Playgroud)

但是如果我调用Clone()一个实例,Sub我会因为object Base.Clone()调用Sub ICloneable<Sub>.Clone()类而遇到StackOverflowException Sub.

问题是协变泛型类型参数.如果我out按预期删除所有作品.

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

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

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

测试代码(不包括界面和BaseSub):

var b = new Sub { StrValue = "Hello World.", IntValue = 42 };
var b2 = (Base)b.Clone();
Run Code Online (Sandbox Code Playgroud)

Yuv*_*kov 5

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

Co/contravariance与语法糖无关.它允许您在编译时将"较小",更具体的类型(协方差)和较大的(逆变)与特定编译器限制一起传递.

因为您的T参数被标记为out,所以CLR将查看任何覆盖实现的运行时Clone.

编译时绑定是一个callvirtto base.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
Run Code Online (Sandbox Code Playgroud)

运行时是多态发生的地方.

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