Lea*_*ner 2 .net c# asp.net c#-4.0
我的烦恼是:在下面的代码提交,它应该显示A
然后B
.但它显示B
然后B
.为什么会这样?
我的感觉是,A
在创建对象时首先执行get的构造函数B
.在那种情况下,方法B
不会正确吗?所以应该A.Display()
而且应该结果A
.此外,然后a.Display()
应该返回B
因为我们有覆盖.
因此,我希望A
再B
.因为它没有超载而是压倒一切.我知道这些东西的定义,我希望了解这种现象的原因以及它是如何工作的内部,因为我不相信有BB
,但AB
.
class A
{
public A()
{
this.Display();
}
public virtual void Display()
{
Console.WriteLine("A");
}
}
class B :A
{
public override void Display()
{
Console.WriteLine("B");
}
}
class C
{
static void Main()
{
A a = new B();
a.Display();
Console.WriteLine();
Console.ReadLine();
}
}
Run Code Online (Sandbox Code Playgroud)
1)在Display
派生类中重写方法时产生以下内容:
A a = new A(); // ---> AA
B a = new B(); // ---> BB // I expect AB.
A a = new B(); // ---> BB // I expect AB.
Run Code Online (Sandbox Code Playgroud)
2)在派生类的方法中使用NEW关键字Display
产生以下结果:
B a = new B(); // ---> AB // I Expect AA here.
A a = new B(); // ---> AA
A a = new A(); // ---> AA
Run Code Online (Sandbox Code Playgroud)
3)更有趣的发现是:
当我base.Display()
在派生构造函数中使用派生类中的基本方法的覆盖时,它给了我BAB
至少在这方面我没有看到任何逻辑.因为,它应该给BBB
我的感觉是,A的构造函数在创建B的对象时首先被执行.
正确.
在那种情况下,B中的方法不会被击中吗?
这是不正确的.
在C++中的类似代码中,您将是正确的.在C++中,有一个规则是在构造对象时构建虚函数调度表.也就是说,当输入"A"构造函数时,vtable用"A"中的方法填充.当控制进入"B"ctor时,然后使用B的方法填充vtable.
在C#中不是这种情况.在C#中,vtable 在对象从内存分配器出来之前填充,在执行ctor之前,并且在此之后它不会改变.该方法的vtable槽始终包含派生最多的方法.
因此,像在这里一样在ctor中调用虚拟方法是一个非常糟糕的主意.可以调用虚方法,其中实现在ctor尚未运行的类上!因此,它可能取决于尚未初始化的州.
请注意,字段初始值设定项在所有ctor体之前运行,幸运的是,对更多派生类的覆盖将始终在覆盖类的字段初始值设定项之后运行.
故事的寓意是:根本不这样做.不要在ctor中调用虚方法.在C++中,您可能会获得与预期不同的方法,在C#中,您可能会获得一个使用未初始化状态的方法.避免,避免,避免.
为什么我们不应该在ctor中调用虚方法?是因为我们总是只在vtable中获得(最新派生的)结果吗?
是.让我用一个例子来说明:
class Bravo
{
public virtual void M()
{
Console.WriteLine("Bravo!");
}
public Bravo()
{
M(); // Dangerous!
}
}
class Delta : Bravo:
{
DateTime creation;
public override void M()
{
Console.WriteLine(creation);
}
public Delta()
{
creation = DateTime.Now;
}
}
Run Code Online (Sandbox Code Playgroud)
好的,所以这个程序的预期行为是,当M
调用any时Delta
,它将打印出实例创建的时间.但事件的顺序new Delta()
是:
Bravo
ctor跑Bravo
ctor打电话 this.M
M
是虚拟的,this
属于运行时类型,Delta
因此Delta.M
运行Delta.M
打印出未初始化的字段,该字段设置为默认时间,而不是当前时间.M
回报Bravo
ctor回来了Delta
ctor设置了这个领域现在,当我说重写方法可能依赖于尚未初始化的状态时,你看到了我的意思吗?在任何其他用法中M
,这都可以,因为Delta
ctor已经完成了.但是这个M
在Delta
ctor开始之前被召唤了!
归档时间: |
|
查看次数: |
3479 次 |
最近记录: |