C#泛型和 - 为什么被称为在基类的方法,而不是在派生类中的新方法?(C# & generics -

2019-07-29 12:42发布

如果通用类型参数(或者是调用类或方法调用的)与受约束where T : Base中衍生的T ==新方法不被调用,而不是基地方法被调用。

为什么T型忽略方法调用,即使它应该运行时间之前知道?

更新 :但是,当该约束,使用等的接口where T : IBase在基类中的方法被调用(未在接口中的方法,这也是不可能的)。
因此,这意味着该系统实际上是能够远远检测的种类和超越类型约束! 那么为什么没有超越类型约束类类型的约束的情况下?
这是否意味着,在基类中实现接口的方法的方法隐含override关键字?

测试代码:

public interface IBase
{
    void Method();
}

public class Base : IBase 
{
    public void Method()
    {

    }
}

public class Derived : Base
{
    public int i = 0;

    public new void Method()
    {
        i++;
    }
}

public class Generic<T>
    where T : Base
{
    public void CallMethod(T obj)
    {
        obj.Method();  //calls Base.Method()
    }

    public void CallMethod2<T2>(T2 obj)
        where T2 : T
    {
        obj.Method();  //calls Base.Method()
    }
}

public class GenericWithInterfaceConstraint<T>
    where T : IBase
{
    public void CallMethod(T obj)
    {
        obj.Method();  //calls Base.Method()
    }

    public void CallMethod2<T2>(T2 obj)
        where T2 : T
    {
        obj.Method();  //calls Base.Method()
    }
}

public class NonGeneric
{
    public void CallMethod(Derived obj)
    {
        obj.Method();  //calls Derived.Method()
    }

    public void CallMethod2<T>(T obj)
        where T : Base
    {
        obj.Method();  //calls Base.Method()
    }

    public void CallMethod3<T>(T obj)
        where T : IBase
    {
        obj.Method();  //calls Base.Method()
    }
}

public class NewMethod
{
    unsafe static void Main(string[] args)
    {
        Generic<Derived> genericObj = new Generic<Derived>();
        GenericWithInterfaceConstraint<Derived> genericObj2 = new GenericWithInterfaceConstraint<Derived>();
        NonGeneric nonGenericObj = new NonGeneric();
        Derived obj = new Derived();

        genericObj.CallMethod(obj);  //calls Base.Method()
        Console.WriteLine(obj.i);

        genericObj.CallMethod2(obj);  //calls Base.Method()
        Console.WriteLine(obj.i);

        genericObj2.CallMethod(obj);  //calls Base.Method()
        Console.WriteLine(obj.i);

        genericObj2.CallMethod2(obj);  //calls Base.Method()
        Console.WriteLine(obj.i);

        nonGenericObj.CallMethod(obj);  //calls Derived.Method()
        Console.WriteLine(obj.i);

        nonGenericObj.CallMethod2(obj);  //calls Base.Method()
        Console.WriteLine(obj.i);

        nonGenericObj.CallMethod3(obj);  //calls Base.Method()
        Console.WriteLine(obj.i);

        obj.Method();  //calls Derived.Method()
        Console.WriteLine(obj.i);
    }
}

输出:

0
0
0
0
1
1
1
2

Answer 1:

使用时,除了dynamic对象,C#总是结合在编译时间的方法-即使使用泛型时。 虚拟方法调用被绑定到虚拟方法时隙而不是向实现方法,这样,当它们在它们将被引导到派生类实现派生类对象上执行; 虽然将在运行时被确定为所述槽指向的方法,于槽的结合发生在编译时间。 如果一个派生类方法被声明new而不是override ,这是使用派生类将使用衍生类方法结合码,但代码这是使用基类将使用基类方法的约束。

为了理解为什么有这样的情况,想象一下,如果它不是。 如果类应该发生什么Base声明的方法int Foo()而一个类Derived:Base宣告一个new string Foo() 如果有约束泛型类T:Base试图调用方法Foo类型的对象上T ,我应该是方法的返回类型是什么?



Answer 2:

这是因为T的约束有语义Base 。 我不能告诉你到底是怎么回事类型在运行时绑定,但是这是我的猜测。

你是不是正确的覆盖方法,而是通过“新的”,而不是隐藏,如果你使用你绕过任何隐藏的基类的引用。 这是隐藏倒下。

如果您使用的是它们所隐藏的类型的引用隐藏其他成员成员只能兑现。 你总是可以通过基类的引用绕过一个隐藏的成员:

var derived = new Derived();
var baseRef = (Base)derived;
baseRef.Method(); // calls Base.Method instead of Derived.Method.

为了正确地重写的方法和具有此代码的工作,标记方法为virtual在基类和override在派生类它。

class Base
{
    public virtual void Method() {}
}

class Derived : Base
{
    public override void Method() {}
}

您可以证明这一点,改变你的通用的限制是where T : Derived ,它应该打出了“新”成员。



Answer 3:

这是由于操作的性质新:新不像覆盖,创建具有相同的名称作为基地之一,这掩盖了基方法,但不覆盖它的功能。

为此,没有一个适当的铸造,原来的方法将被如果基准的类型是基本的调用。



Answer 4:

new关键字只是隐藏的方法,而不是超载。 你非一般的原因CallMethod出现预期的工作是因为该方法的签名需要一个Derived的,而不是一个Base

泛型是不是真的在这里的罪魁祸首。 如果你改变了方法签名CallMethod(Base obj) ,你会看到同样的“意外”的行为与一般的实现和得到以下的输出:

0
0
0
0
0
0
0
1

如果您Base.Method虚拟与覆盖它Derived.Method像这样:

public class Base 
{
    public virtual void Method()
    {

    }
}

public class Derived : Base
{
    public int i = 0;

    public override void Method()
    {
        i++;
    }
}

你会得到下面的输出:

1
2
3
4
5
6
7
8

编辑:更新,以匹配问题的更新的输出。



文章来源: C# & generics - why is method in base class called instead of new method in derived class?