覆盖和隐藏之间的确切区别
任何人都可以在内存和引用方面告诉覆盖和隐藏的工作。
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
但是被赋予了不同的名称,那就是它。) Test2
的实现仍然在它自己的插槽中“存在”; 它被隐藏而不是被覆盖。 如果将B
的实例视为B
,则得到B.Test2
; 如果你把它投射到A
,你就看不到新的插槽,并且A.Test2
。
要添加@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"; } } }
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语义的静态方法,这意味着它也不会在内存中添加任何大小的实例。
已经在这里回答了
覆盖是相同方法签名的多个可能实现的定义,这样实现由第0个参数的运行时类型决定(通常由C#中的名称标识)。
隐藏是派生类型中方法的定义,其签名与其基本类型中的签名相同,而不会覆盖。
覆盖和隐藏之间的实际区别如下:
隐藏适用于所有其他成员(静态方法,实例成员,静态成员)。 它基于早期绑定。 更清楚的是,在编译期间决定要调用或使用的方法或成员。
•如果重写方法,则要调用的实现基于参数的运行时类型。 •如果只隐藏方法,则要调用的实现基于参数this的编译时类型。
以下是一些示例: 示例#1 。 和例子#2
类A中的Test1()方法和类B中的test1()方法将根据MethdOverriding执行。
类A中的Test2()方法和类B中的test2()方法将根据方法隐藏执行。
在方法中重写子类成员将执行,并且在方法隐藏中 ,父类成员将执行。
简单地说,当覆盖方法或属性时,覆盖方法必须与基本方法具有相同的签名。 当隐藏它不是必需的时,新对象可以采用如下所示的任何forms
// base public int GrossAmount { get; set; } // hiding base public new string GrossAmount { get; set; }
从提供的代码中扣除您应该有B:A
。
你可以隐藏一个方法,以防你想要创建自己的基类的(比方法)实现,这个方法不能被覆盖,因为它不是virtual
。
在我的经验中,我主要使用隐藏来进行debug
。
例如,当我不知道是谁设置了某些第3个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(); } }
方法隐藏是指Base Class引用变量指向子类对象时。 它将调用基类中的隐藏方法。
当我们在基类中声明虚方法时。 我们在derived类或子类中重写该方法。 然后Base Class引用变量将调用派生类方法。 这称为方法覆盖。
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(); } }
输出: –
加法儿童
乘以儿童
划分孩子
加法基础
乘以儿童
划分基数
加法基础
乘以基数
划分基数
在重写时,编译器会检查类的对象,但在隐藏编译器时只检查类的引用