17

任何人都可以根据内存和引用来告诉覆盖和隐藏的工作。

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------------------------
}

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

任何非常深入和完整地描述这个基本原理的链接/练习都会有很大的帮助。

4

9 回答 9

31

看看Eric Lippert对另一个问题的回答。

套用(在我的理解范围内),这些方法进入“槽”。A有两个插槽:一个 forTest1和一个 for Test2

由于A.Test1被标记为virtualB.Test1被标记为overrideB的实现Test1不会创建自己的插槽,而是覆盖A的实现。无论您将 的实例B视为 aB还是将其强制转换为A,相同的实现都在该插槽中,因此您总是得到B.Test1.

相比之下,由于B.Test2是被标记new的,它会创建自己的槽。(就像它没有被标记new但被赋予了不同的名称一样ATest2)它被隐藏而不是被覆盖。如果将 的实例B视为 a B,则会得到B.Test2; 如果将其转换为A,则看不到新插槽,A.Test2并被调用。

于 2012-06-15T07:57:19.633 回答
6

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

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"; }
    }
}

1. 压倒一切

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

{
    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"
}

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"
}

三、总结

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

4. 实例化后的对象大小

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

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

于 2012-06-15T08:42:19.610 回答
3

已经在这里回答了

覆盖是同一方法签名的多个可能实现的定义,这样实现由第零个参数的运行时类型确定(在 C# 中通常由名称 this 标识)。

隐藏是派生类型中方法的定义,其签名与其基类型之一中的签名相同,但没有覆盖。

覆盖和隐藏之间的实际区别如下:

隐藏适用于所有其他成员(静态方法、实例成员、静态成员)。它基于早期绑定。更清楚地说,要调用或使用的方法或成员是在编译时决定的。

• 如果方法被覆盖,调用的实现基于参数this 的运行时类型。• 如果一个方法被简单地隐藏,调用的实现是基于参数this 的编译时类型。

下面是一些示例:示例#1。和示例#2

于 2012-06-15T07:39:41.170 回答
1

A 类中的 Test1() 方法和 B 类中的 test1() 方法将根据MethdOverriding执行。

A 类中的 Test2() 方法和 B 类中的 test2() 方法将根据Method Hiding执行。

在方法覆盖中,子类成员将执行,在方法隐藏中,父类成员将执行。

于 2014-07-11T09:48:05.657 回答
0

从提供的代码中扣除您应该拥有的B:A.

您可以隐藏一个方法,以防您想创建自己的基类(比如说)方法的实现,该方法不能被覆盖,因为,比如说,它不是virtual.

在我的经验中,我使用隐藏 主要是为了debug目的。

例如,当我不知道谁设置了某些 3rd prt 的属性时,component我无法使用谁的代码。所以我要做的是:

  • 从组件创建一个子类
  • new用关键字隐藏感兴趣的属性
  • 将断点放入set
  • 并等待它何时被击中。

有时,非常有用,可以帮助我快速获取信息,尤其是在您学习新知识的第一阶段componentsframeworks...libraries

于 2012-06-15T07:33:32.030 回答
0

简单地说,在覆盖方法或属性时,覆盖方法必须与基方法具有相同的签名。当不需要隐藏它时,新对象可以采用如下所示的任何形式

// base
    public int GrossAmount { get; set; }

    // hiding base
    public new string GrossAmount
    {
        get;
        set;             
    }
于 2012-06-15T07:36:38.297 回答
0

通过隐藏方法或属性,您只是在声明当您拥有该类型的对象时,您希望停止此类方法是多态的。此外,隐藏方法以非多态方式调用,因此必须在编译时知道调用这些方法类型,因为它是一个简单的非虚拟方法。

于 2012-06-15T07:47:13.367 回答
0
 public class BaseClass
    {
      public void PrintMethod()
      {
       Console.WriteLine("Calling base class method");
      }
     }
     public class ChildClass
     {
      public new void PrintMethod()
      {
       Console.WriteLine("Calling the child or derived class method");
       }
      }
      class Program
      {
       static void Main()
       {
        BaseClass bc = new ChildClass();
        bc.PrintMethod();
        }
       }

Method Hiding 就是当基类引用变量指向子类对象时。它将调用基类中的隐藏方法。

其中,当我们在基类中声明虚方法时。我们在派生类或子类中覆盖该方法。然后基类引用变量将调用派生类方法。这称为方法覆盖。

于 2015-05-07T08:05:37.700 回答
0
class Base {
    int a;
    public void Addition() {
        Console.WriteLine("Addition Base");
    }
    public virtual void Multiply()
    {
        Console.WriteLine("Multiply Base");
    }
    public void Divide() {
        Console.WriteLine("Divide Base");
    }
}

class Child : Base
{
    new public void Addition()
    {
        Console.WriteLine("Addition Child");
    }
    public override void Multiply()
    {
        Console.WriteLine("Multiply Child");
    }
    new public void Divide()
    {
        Console.WriteLine("Divide Child");
    }
}
class Program
{        
    static void Main(string[] args)
    {
        Child c = new Child();
        c.Addition();
        c.Multiply();
        c.Divide();

        Base b = new Child();
        b.Addition();
        b.Multiply();
        b.Divide();

        b = new Base();
        b.Addition();
        b.Multiply();
        b.Divide();
    }
}

输出 : -

加法子

乘子

分孩子

加法基础

乘子

划分基地

加法基础

乘基础

划分基地

在覆盖编译器时检查类的对象,但在隐藏编译器只检查类的引用

于 2017-07-28T07:41:18.003 回答