任何人都可以告诉我们在内存和引用方面的覆盖和隐藏工作。
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的方法(在我看来)。
任何非常深刻和完整地描述这一基础的链接/练习都将是一个很大的帮助。
答案 0 :(得分:27)
看看Eric Lippert的this answer to a different question。
为了解释(达到我理解的极限),这些方法进入“插槽”。 A
有两个广告位:一个用于Test1
,另一个用于Test2
。
由于A.Test1
标记为virtual
且B.Test1
标记为override
,B
的{{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
有时,非常有用,可以帮助我快速获取信息,特别是在您学习新的set
,components
,frameworks
时,第一阶段。
答案 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
划分基数
在重写时,编译器会检查类的对象但是在 隐藏编译器只检查类的引用