首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用具有协变量泛型参数的StackOverflowException接口实现

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

Stack Overflow用户
提问于 2015-09-15 19:33:35
回答 1查看 122关注 0票数 3

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

代码语言:javascript
运行
复制
public interface ICloneable<out T> : ICloneable
{
    new T Clone();
}

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

代码语言:javascript
运行
复制
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>,以返回这个新类型:

代码语言:javascript
运行
复制
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类的Sub ICloneable<Sub>.Clone()

问题是协变量泛型类型参数。如果我删除了out,那么所有操作都按预期进行。

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

协方差和反方差是否只有语法糖,编译器在派生层次结构中搜索最低可能的类型?这意味着编译器将将ICloneable<Base>更改为ICloneable<Sub>

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

测试代码(不包括接口以及BaseSub):

代码语言:javascript
运行
复制
var b = new Sub { StrValue = "Hello World.", IntValue = 42 };
var b2 = (Base)b.Clone();
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-09-15 20:06:44

协方差和反方差是否只有语法糖,编译器在派生层次结构中搜索最低可能的类型?

Co/反方差与句法糖无关。它允许您在编译时传递具有特定编译器限制的“较小”、更特定类型(协方差)和较大类型(反方差)。

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

编译时绑定是一个callvirtbase.Clone,这不会改变:

代码语言:javascript
运行
复制
.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

运行时是多态发生的位置.

移除out的事实恰恰强调了这一点。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32594179

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档