为什么这个泛型方法调用基类方法,而不是派生类方法

本文关键字:类方法 派生 基类 泛型方法 调用 为什么 | 更新日期: 2023-09-27 18:16:09

对于以下代码:

class B
{
    public String G() { return "B.G()"; }
}
class D : B
{
    public String G() { return "D.G()"; }
}
class TestCompile
{
    private static String TestG<T>(T b) where T: B 
    {
        return b.G();
    }
    static void Main(string[] args)
    {
        TestG(new D());
    }
}

结果是B.G(),而类似的c++代码的结果将是D.G()

为什么会有这样的区别?

为什么这个泛型方法调用基类方法,而不是派生类方法

使用覆盖关键字:

class B
{
    public virtual String G() { return "B.G()"; }
}
class D : B
{
    public override String G() { return "D.G()"; }
}

如果没有override关键字,继承的方法不会替换基方法。

没有覆盖:

D obj = new D();
obj.G(); // "D.G()"
((B)obj).G(); // "B.G()"
与覆盖:

D obj = new D();
obj.G(); // "D.G()"
((B)obj).G(); // "D.G()"

c#泛型只编译一次:在编译泛型的时候。(想想看:c#让你不用看到List<T>的实现就可以使用它。)这里,从where T: B子句中看到参数是B,所以调用B.G

c++模板在每次被调用时都会被编译。当您输入TestG<D>()时,将使用T = D编译一个全新的TestG副本。在调用时,编译器看到D有自己的G方法并调用它。

c++中与c#泛型等价的是

template<typename T>
string TestG(T t)
{
    B& b = static_cast<B&>(t); // force `t` into a `B`
    return b.G();
}

其他人关于使用virtual的评论同样适用于c#和c++。

因为您忘记将B.G标记为virtual,将D.G标记为override

你得到了这个编译器警告:

CS0108: 'D.G()'隐藏继承成员'B.G()'。如果要隐藏,请使用new关键字。

但是你选择忽略它。我对c++开发人员有更好的期望!:)

这是因为参数bB类型ref.如果您将b强制转换为D,它将调用D上的函数。