覆盖和隐藏之间的确切区别

Akk*_*i J 16 c# oop overriding member-hiding

任何人都可以在内存和引用方面告诉覆盖和隐藏的工作.

class A
{
    public virtual void Test1() { //Impl 1}
    public virtual void Test2() { //Impl 2}
}
class B  : A
{
    public override void Test1() { //Impl 3}
    public new void Test2() { Impl 4}
}

static Main()
{
    A aa=new B() //This will give memory to B
    aa.Test1(); //What happens in terms of memory when this executes
    aa.Test2(); //-----------------------SAME------------------------
}
Run Code Online (Sandbox Code Playgroud)

这里的内存是B类,但在第二个语句中,将调用aa.Test2类A的方法.为什么?如果B有内存,则应调用B的方法(在我看来).

任何非常深刻和完全描述这一基础的链接/练习将是一个很大的帮助.

Raw*_*ing 27

看看Eric Lippert 对这个问题的回答.

为了解释(达到我的理解的极限),这些方法进入"槽".A有两个插槽:一个用于Test1,一个用于Test2.

由于A.Test1被标记为virtual并被B.Test1标记为override,所以B实现Test1不会创建自己的插槽而是覆盖其A实现.无论您将实例B视为a B还是将其转换为a A,同一实现都在该插槽中,因此您始终可以得到结果B.Test1.

相比之下,由于B.Test2标记new,它创建了自己的插槽.(如果它没有被标记,new但被赋予了不同的名称,那就好了.)它A的实现Test2仍然在它自己的插槽中"存在"; 它被隐藏而不是被覆盖.如果你把一个实例B视为a B,你会得到B.Test2; 如果你把它投射到一个A,你看不到新的插槽,A.Test2并被调用.


Gro*_*roo 5

要添加@Rolling的答案,可以使用如下示例显示实际示例:

class Base
{
    // base property
    public virtual string Name
    {
        get { return "Base"; }
    }
}

class Overriden : Base
{
    // overriden property
    public override string Name
    {
        get { return "Overriden"; }
    }
}

class New : Base
{
    // new property, hides the base property
    public new string Name
    {
        get { return "New"; }
    }
}
Run Code Online (Sandbox Code Playgroud)

1.压倒一切

overriden属性的情况下,基类的虚方法槽由不同的实现替换.编译器将该方法视为虚拟,并且必须使用对象的虚拟表在运行时解析其实现.

{
    Base b = new Base();
    Console.WriteLine(b.Name); // prints "Base"

    b = new Overriden();
    // Base.Name is virtual, so the vtable determines its implementation
    Console.WriteLine(b.Name); // prints "Overriden"

    Overriden o = new Overriden();
    // Overriden.Name is virtual, so the vtable determines its implementation
    Console.WriteLine(o.Name); // prints "Overriden"
}
Run Code Online (Sandbox Code Playgroud)

2.隐藏

使用关键字隐藏方法或属性时new,编译器仅为派生类创建新的非虚方法; 基类的方法保持不变.

如果变量的类型是Base(即仅包含虚方法),则其实现将通过vtable解析.如果变量的类型是New,则将调用非虚方法或属性.

{
    Base b = new Base();
    Console.WriteLine(b.Name); // prints "Base"

    b = new New();
    // type of `b` variable is `Base`, and `Base.Name` is virtual,
    // so compiler resolves its implementation through the virtual table
    Console.WriteLine(b.Name); // prints "Base"

    New n = new New();
    // type of `n` variable is `New`, and `New.Name` is not virtual,
    // so compiler sees `n.Name` as a completely different property
    Console.WriteLine(n.Name); // prints "New"
}
Run Code Online (Sandbox Code Playgroud)

3.总结

如果代码的一部分接受基类型,它将始终在运行时使用虚拟表.对于大多数OOP场景,这意味着将方法标记new为非常类似于给它一个完全不同的名称.

4.实例化后的对象大小

请注意,实例化任何这些类型都不会创建虚拟表的副本.每个.NET对象都有几个字节的头和一个指向其类型(class)的表的虚拟表的指针.

关于new属性(非虚拟的),它基本上被编译为具有thiscall语义的静态方法,这意味着它也不会向内存中的实例大小添加任何内容.