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

时间:2012-06-15 07:30:28

标签: c# oop override 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------------------------
}

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

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

9 个答案:

答案 0 :(得分:27)

看看Eric Lippert的this answer to a different question

为了解释(达到我理解的极限),这些方法进入“插槽”。 A有两个广告位:一个用于Test1,另一个用于Test2

由于A.Test1标记为virtualB.Test1标记为overrideB的{​​{1}}实施不会创建自己的插槽,但覆盖Test1的实现。无论您将A的实例视为B还是将其投放到B,相同的实现都在该广告位中,因此您始终可以获得A的结果。

相比之下,由于B.Test1标记为B.Test2,因此会创建自己的广告位。 (如果它没有被标记为new但是被赋予了不同的名称。)new A的实现仍然在其自己的位置“存在”;它被隐藏而不是被覆盖。如果您将Test2的实例视为B,则会获得B;如果您将其投放到B.Test2,则无法看到新广告位,并且A会被调用。

答案 1 :(得分:5)

要添加到@Rawling's answer,可以使用以下示例显示实际示例:

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。覆盖

如果是 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"
}

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

第3。摘要

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

<强> 4。实例化后的对象大小

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

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

答案 2 :(得分:3)

已在here

处回答

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

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

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

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

•如果方法被覆盖,则要调用的实现基于参数的运行时类型。 •如果只是隐藏方法,则要调用的实现基于参数的编译时类型。

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

答案 3 :(得分:1)

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

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

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

答案 4 :(得分:0)

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

你可以隐藏一个方法,以防你想要创建你自己的基类的(比方法)实现,不能被覆盖,因为它不是{{1 }}。

在我的经验中,我使用隐藏主要用于virtual目的。

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

  • 从组件
  • 创建子类
  • 使用component关键字
  • 隐藏感兴趣的媒体资源
  • 将断点放在new
  • 并等待它会被击中。

有时,非常有用,可以帮助我快速获取信息,特别是在您学习新的setcomponentsframeworks时,第一阶段。

答案 5 :(得分:0)

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

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

    // hiding base
    public new string GrossAmount
    {
        get;
        set;             
    }

答案 6 :(得分:0)

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

答案 7 :(得分: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();
        }
       }

方法隐藏是指Base Class引用变量指向子类对象时。它将调用基类中的隐藏方法。

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

答案 8 :(得分: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();
    }
}

输出: -

加法儿童

Multiply Child

分孩子

加法基础

Multiply Child

划分基数

加法基础

Multiply Base

划分基数

在重写时,编译器会检查类的对象但是在 隐藏编译器只检查类的引用