关于C#:覆盖和隐藏之间的确切区别overriding and hiding

Exact difference between overriding and hiding

任何人都能从记忆和参考的角度来判断重写和隐藏的工作原理吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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的方法(在我看来)。

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


看看埃里克·利珀特的另一个问题的答案。

为了解释(在我的理解范围内),这些方法进入"槽"。A有两个插槽:一个用于Test1,一个用于Test2

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

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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属性的情况下,基类的虚方法槽将被另一个实现替换。编译器将该方法视为虚拟的,并且必须在运行时使用对象的虚拟表解析其实现。

1
2
3
4
5
6
7
8
9
10
11
12
{
    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,那么将调用非虚拟方法或属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
    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属性(非虚拟属性),它基本上是用这个调用语义编译成一个静态方法,这意味着它也不会为内存中实例的大小添加任何内容。


已经在这里回答了

重写是对同一方法签名的多个可能实现的定义,这样实现就由第零个参数的运行时类型(通常由C中的名称标识)决定。

隐藏是派生类型中方法的定义,其签名与它的一个基类型中的签名相同,但不进行重写。

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

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

?如果方法被重写,则要调用的实现基于此参数的运行时类型。?如果方法只是隐藏的,则要调用的实现基于参数this的编译时类型。

以下是一些示例:示例1。例2


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

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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();
    }
}

输出:

加法儿童

乘法儿童

分裂儿童

加碱

乘法儿童

分基

加碱

乘法基数

分基

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 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();
        }
       }

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

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


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


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

1
2
3
4
5
6
7
8
9
// base
    public int GrossAmount { get; set; }

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

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

您可以隐藏一个方法,以防需要创建自己的基类(say)方法的实现,而该方法不能被重写,因为,例如,它不是virtual

在我生命的最后阶段,我主要是为了江户的目的而躲藏。

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

  • 从组件创建子类
  • 使用new关键字隐藏感兴趣的属性
  • 把断点放在set
  • 等它被击中。

有时非常有用,可以帮助我快速获取信息,尤其是在学习新的componentsframeworkslibraries的第一阶段。无论什么。