当前位置: 首页 > news >正文

C# 小测试(一):类成员初始化与构造函数执行的顺序

看看下面这段代码,你觉得它会输出什么呢?

    class Foo
    {
        public Foo(string s)
        {
            Console.WriteLine("Foo constructor: {0}", s);
        }
 
        public void Bar(){}
    }
 
    class Base
    {
        readonly Foo baseFoo = new Foo("Base initializer");
        public Base()
        {
            Console.WriteLine("Base constructor");
        }
    }
 
    class Derived : Base
    {
        readonly Foo derivedFoo = new Foo("Derived initializer.");
        public Derived()
        {
            Console.WriteLine("Derived constructor");
        }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            new Derived();
        }
    }

先猜一下吧,似乎应该是“Base initializer, Base constructor, Derived initializer, Derived constructor”。

事实上,应当是先执行类成员的初始化,顺序是从derived到base,然后是两个构造函数,顺序是从base从derived。

这种方式是很有意义的,在类继承体系中层次较深的类(离System.Object较远)将依赖于较浅的类(离System.Object较近)。但是很多人会相信调用的顺序应当等价于下面的伪代码:

// 期望的顺序
BaseConstructor()
{
    ObjectConstructor();
    baseFoo = new Foo("Base initializer");
    Console.WriteLine("Base constructor");
}
DerivedConstructor()
{
    BaseConstructor();
    derivedFoo = new Foo("Derived initializer");
    Console.WriteLine("Derived constructor");
}

而实际情况则是:

 // 实际的顺序
BaseConstructor()
{
    baseFoo = new Foo("Base initializer");
    ObjectConstructor();
    Console.WriteLine("Base constructor");
}
DerivedConstructor()
{
    derivedFoo = new Foo("Derived initializer");
    BaseConstructor();
    Console.WriteLine("Derived constructor");
} 

那么,这样处理是为什么呢?

...

...

...

我们来看一下,如果代码按期望的顺序(第一段伪代码)执行,会产生什么问题:

class Base
{
    public Base()
    {
        Console.WriteLine("Base constructor");
        if (this is Derived) (this as Derived).DoIt();
        // 如果是在创建Derived类的实例,就会遭遇null。
        Blah();
        // 如果是在创建MoreDerived类的实例,就会遭遇null。
    }
 
    public virtual void Blah() { }
}
 
class Derived : Base
{
    readonly Foo derivedFoo = new Foo("Derived initializer");
    public DoIt()
    {
        derivedFoo.Bar();
    }
}
 
class MoreDerived : Derived
{
    public override void Blah() { DoIt(); }
} 

看Base类的构造函数,如果按期望的顺序执行,那么在Base方法执行时,Derived类的实例成员并没有得到初始化,此时就会有NullReference异常了。

而按照实际执行的顺序,所有的实例成员都能确保被完整地初始化:)

当然了,如果readonly字段是在构造函数中进行的,那么上面的确保机制就不复存在了。

参考:

Why Do Initializers Run In The Opposite Order As Constructors? Part One 

Why Do Initializers Run In The Opposite Order As Constructors? Part Two


本文转自一个程序员的自省博客园博客,原文链接:http://www.cnblogs.com/anderslly/archive/2008/07/12/Why-Do-Initializers-Run-In-The-Opposite-Order-As-Constructors.html,如需转载请自行联系原作者。

相关文章:

  • debian中安装和编译ipvsadm问题
  • kubernetes 1.8 高可用安装(三)
  • Putty中为CentOS 5.5配置SSH证书登录验证(转)
  • 写JS的时候,想强制刷新页面,有些代码却不能很好的兼容
  • 选eMTC还是NB-IoT,不应该再是一个问题
  • HTML教程
  • 总结: 在fc23中, 安装音频mp3 视频flv 的播放插件其实很简单, 只要一步就可以了: dnf install gstreamer1-libav...
  • jxl操作excel(每页200个,每行4个)
  • 为什么ios不支持flash
  • C# Dictionary 的几种遍历方法
  • Hadoop的MapReduce执行流程图
  • ARM9学习笔记之——MMU
  • Git-Basic
  • 垃圾邮件年关“疯行” 邮件服务器安全管理巧支招
  • dd
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • If…else
  • Java 内存分配及垃圾回收机制初探
  • JavaScript标准库系列——Math对象和Date对象(二)
  • KMP算法及优化
  • PHP 7 修改了什么呢 -- 2
  • springboot_database项目介绍
  • Unix命令
  • 安卓应用性能调试和优化经验分享
  • 百度贴吧爬虫node+vue baidu_tieba_crawler
  • 分布式事物理论与实践
  • 跨域
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 使用 Xcode 的 Target 区分开发和生产环境
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • PostgreSQL之连接数修改
  • Semaphore
  • #大学#套接字
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (附表设计)不是我吹!超级全面的权限系统设计方案面世了
  • (论文阅读30/100)Convolutional Pose Machines
  • (转)shell调试方法
  • .dat文件写入byte类型数组_用Python从Abaqus导出txt、dat数据
  • .net MVC中使用angularJs刷新页面数据列表
  • .net 微服务 服务保护 自动重试 Polly
  • .NetCore实践篇:分布式监控Zipkin持久化之殇
  • .NET性能优化(文摘)
  • .net中我喜欢的两种验证码
  • @拔赤:Web前端开发十日谈
  • [ 代码审计篇 ] 代码审计案例详解(一) SQL注入代码审计案例
  • [Android Pro] listView和GridView的item设置的高度和宽度不起作用
  • [Android] Android ActivityManager
  • [Angular 基础] - 指令(directives)
  • [Angular 基础] - 自定义指令,深入学习 directive
  • [C/C++]数据结构 深入挖掘环形链表问题
  • [emuch.net]MatrixComputations(7-12)
  • [EULAR文摘] 脊柱放射学持续进展是否显著影响关节功能
  • [flume$2]记录一个写自定义Flume拦截器遇到的错误
  • [Flutter]设置应用包名、名称、版本号、最低支持版本、Icon、启动页以及环境判断、平台判断和打包
  • [jquery]this触发自身click事件,当前控件向上滑出