覆盖和隐藏之间的确切区别
本文关键字:区别 之间 隐藏 覆盖 | 更新日期: 2023-09-27 18:37:11
谁能从内存和引用的角度分辨出覆盖和隐藏的工作。
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 的方法(在我看来)。
任何非常深入和完整地描述这一基本原理的链接/练习都将是一个很大的帮助。
看看Eric Lippert对另一个问题的回答。
套用(在我的理解范围内),这些方法进入"插槽"。 A
有两个插槽:一个用于Test1
,一个用于Test2
。
由于A.Test1
被标记为virtual
而B.Test1
被标记为override
,B
的Test1
实现不会创建自己的插槽,而是覆盖A
的实现。无论您是将B
实例视为B
还是将其转换为A
,该插槽中都有相同的实现,因此您始终会得到B.Test1
的结果。
相比之下,由于B.Test2
被标记为new
,它会创建自己的新插槽。(如果它没有被标记为new
而是被赋予了不同的名称。 A
Test2
的实现仍然"存在"在自己的插槽中;它已被隐藏而不是覆盖。如果将B
的实例视为B
,则会得到B.Test2
;如果将其投射到A
,则看不到新插槽,并且A.Test2
被调用。
为了补充@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. 覆盖
对于重写属性,基类的虚拟方法的插槽将替换为不同的实现。编译器将该方法视为虚拟方法,并且必须在运行时使用对象的虚拟表解析其实现。
{
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 语义的静态方法,这意味着它也不会为内存中实例的大小添加任何内容。
已经在这里回答了
重写是同一方法签名的多个可能实现的定义,以便实现由第 0 个参数的运行时类型确定(通常由 C# 中的名称 this 标识)。
隐藏是派生类型中方法的定义,该方法的签名与其基类型之一中的签名相同,而不重写。
覆盖和隐藏之间的实际区别如下:
隐藏适用于所有其他成员(静态方法、实例成员、静态成员)。它基于早期绑定。更清楚的是,要调用或使用的方法或成员是在编译时决定的。
•如果方法被重写,则要调用的实现基于参数 this 的运行时类型。•如果方法只是隐藏,则要调用的实现基于参数 this 的编译时类型。
以下是一些示例:示例#1.和示例#2
中的Test1()方法和类B中的test1()方法将根据MethdOverrideing执行。
类 A 中的 Test2() 方法和类 B 中的 test2() 方法将根据方法隐藏执行。
在方法重写中,将执行子类成员,在"隐藏方法"中,将执行父类成员。
简单地说,在重写方法或属性时,重写方法必须与基方法具有相同的签名。当隐藏它不是必需的时,新对象可以采用任何形式,如下所示
// base
public int GrossAmount { get; set; }
// hiding base
public new string GrossAmount
{
get;
set;
}
从提供的代码中扣除您应该有B:A
.
您可以隐藏一个方法,以防您想要创建自己的基类的(say)方法的实现,该方法不能被覆盖,因为,比如说,它不是virtual
。
在我去世的时候,我主要出于debug
目的而使用隐藏。
例如,当我不知道谁设置了某个 3rd prt component
的属性时,我无法使用谁的代码。所以我所做的是:
- 从组件创建子类
- 使用关键字隐藏感兴趣的属性
new
- 将断点放在
set
- 并等待它被击中时。
有时,非常有用,可以帮助我快速获取信息,尤其是在第一阶段,当您学习新components
,frameworks
,libraries
.等等。
通过隐藏方法或属性,您只是在声明当您具有该类型的对象时,您希望阻止此类方法的多态性。此外,隐藏方法以非多态方式调用,因此必须在编译时知道调用这些方法类型,因为它只是一种非虚拟方法。
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();
}
}
方法隐藏是指当基类引用指向子类对象的变量时。它将调用基类中的隐藏方法。
其中,当我们在基类中声明虚拟方法时。我们在派生类或子类中重写该方法。然后基类引用变量将调用派生类方法。这称为方法重写。
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();
}
}
输出:-
加法子项
乘以子级
划分子项
加法底座
乘以子级
划分基地
加法底座
乘基数
划分基地
在重写时,编译器会检查类的对象,但在隐藏编译器仅检查类的引用