首页 > C# > 构造函数中的虚拟成员调用

构造函数中的虚拟成员调用

上一篇 下一篇

我收到来自 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!?!
    }
}

答案是,实际上 a 将被抛出,因为 是 null。对象的基构造函数在其自己的构造函数之前调用。通过在对象的构造函数中进行调用,您引入了继承对象在完全初始化之前执行代码的可能性。NullReferenceExceptionfoovirtual

分割线

网友回答:

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 之间的规则差异很大。您的程序员可能不知道会发生什么!

模板简介:该模板名称为【构造函数中的虚拟成员调用】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【C#】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 318次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 02-15
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
微信公众平台 微信文章 企业网站 响应式 html5 自适应 微信图片 单页式简历模板 微信素材 微信模板
您可能会喜欢的其他模板