重写虚拟方法时缺少返回类型协方差的解决方法

本文关键字:方法 方差 返回类型 解决 虚拟 重写 | 更新日期: 2023-09-27 18:21:39

是否有任何方法可以"破解"或"强制"C#中的协变重写?

例如:

public class Alpha {
    public virtual Alpha DoSomething() {
        return AlphaFactory.GetAlphaFromSomewhere();
    }
}
public class Beta : Alpha {
    public override Beta DoSomething() {
        return BetaFactory.GetBetaFromSomewhere();
    }
}

不幸的是,C#不支持这一点(这看起来有点可笑,但这既不存在也不存在)。

我想我可能有一个隐藏方法的答案:

new public Beta DoSomething() {
    return BetaFactory.GetBetaFromSomewhere();
}

但这并没有将条目添加到"vtable"中,它只是基本上声明了一个具有相同名称的全新方法,因此意味着通过指向Alpha的指针访问Beta将调用Alpha.DoSomething()

那么,有什么好把戏吗?

重写虚拟方法时缺少返回类型协方差的解决方法

您可以用泛型做一些非常滑稽的事情。

public class Alpha<T> where T: Alpha<T> {
    public virtual T DoSomething() {
        throw new NotImplementedException();
    }
}
public class Beta : Alpha<Beta> {
    public override Beta DoSomething() {
        throw new NotImplementedException();
    }
}

您可以始终将方法包装在另一个方法中

public class Alpha {
    public virtual Alpha DoSomething() {
        return AlphaFactory.GetAlphaFromSomewhere();
    }
}
public class Beta : Alpha {
    private Beta Helper() {
        return BetaFactory.GetBetaFromSomewhere();
    }
    public Beta DoSomething() {
        return Helper();
    }
    public override Alpha DoSomething() {
        return Helper();
    }
}

如果有很多方法,您可能需要自动生成这些包装器。如果要求太高,那么就只剩下@recursive的解决方案了。

因为在大多数情况下,返回类型协方差没有太大的好处。

如果你正在调用虚拟Alpha.DoSomething,那么你真的不知道你是要返回Alpha还是Beta,那么返回类型协方差有什么帮助?无论如何,您都必须将结果存储在Alpha类型的对象中,因为它是唯一可以允许Alpha s或Beta s的类型。

另一方面,如果静态地知道您正在调用Beta.DoSomething,那么您可以:直接使用返回的Alpha类型的对象,因为您将访问通用功能或虚拟方法,所以您不在乎它是否真的是Beta(虚拟调用将解析为正确的类型),或者您可以直接选择(运行时安全)强制转换为Beta以访问特定的实施

编辑:删除类型安全问题。我几乎没有想过,显然返回型协方差是安全的。剩下的答案仍然成立。我在这个功能中找不到任何特别有趣的东西,至少在OP想要使用它的方式上是这样的:避免安全廉价的演员阵容?