在构造函数中调用虚拟成员
我从ReSharper收到关于从我的对象构造函数调用虚拟成员的警告。
为什么这是不该做的事情?
当用C#编写的对象被构造时,会发生什么是初始化器从大多数派生类到基类的顺序运行,然后构造函数按从基类到最派生类的顺序运行(请参阅Eric Lippert的博客以了解详细信息至于为什么这是)。
在.NET中,对象也不会在构造时改变类型,但是从最为派生的类型开始,方法表用于最派生的类型。 这意味着虚拟方法调用总是运行在最派生的类型上。
当你将这两个事实结合起来的时候,你会遇到这样的问题:如果你在构造函数中进行虚拟方法调用,并且它不是继承层次结构中派生最多的类型,那么它将在其构造函数未被构造的类上被调用运行,因此可能不处于适当的状态来调用该方法。
如果将类标记为密封,以确保它是继承层次结构中派生最多的类型,则此问题当然会得到缓解 - 在这种情况下,调用虚拟方法是非常安全的。
为了回答你的问题,考虑这个问题:当Child
对象被实例化时,下面的代码会打印出什么?
class Parent
{
public Parent()
{
DoSomething();
}
protected virtual void DoSomething()
{
}
}
class Child : Parent
{
private string foo;
public Child()
{
foo = "HELLO";
}
protected override void DoSomething()
{
Console.WriteLine(foo.ToLower());
}
}
答案是,实际上会抛出NullReferenceException
,因为foo
为空。 一个对象的基础构造函数在它自己的构造函数之前被调用 。 通过在对象的构造函数中进行virtual
调用,您可以引入继承对象在完全初始化之前执行代码的可能性。
C#的规则与Java和C ++的规则非常不同。
当您在C#中某个对象的构造函数中时,该对象以完全派生类型的形式存在于完全初始化(不是“构造”)窗体中。
namespace Demo
{
class A
{
public A()
{
System.Console.WriteLine("This is a {0},", this.GetType());
}
}
class B : A
{
}
// . . .
B b = new B(); // Output: "This is a Demo.B"
}
这意味着,如果您从A的构造函数调用虚函数,它将解析为B中的任何覆盖(如果提供了覆盖)。
即使你这样故意设置A和B,充分理解系统的行为,你以后可能会感到震惊。 假设你在B的构造函数中调用了虚函数,“知道”它们将由B或A酌情处理。 然后时间流逝,而其他人决定他们需要定义C,并覆盖那里的一些虚拟功能。 突然之间B的构造函数最终在C中调用代码,这可能导致相当令人惊讶的行为。
无论如何,避免构造函数中的虚函数可能是一个好主意,因为C#,C ++和Java之间的规则是如此不同。 你的程序员可能不知道该期待什么!
链接地址: http://www.djcxy.com/p/14149.html上一篇: Virtual member call in a constructor
下一篇: Performance differences between debug and release builds