开发者社区> 长征2号> 正文

C#成员初始化有点坑爹

简介:
+关注继续查看

C#成员的初始化顺序你真的非常清楚吗,我发现有点坑爹,坑到爹突然有点搞不清楚什么状况。下面咱们开始分析,先看3个简单类。

 

复制代码
    public abstract class Base
    {
        public Base()
        {
            SetValue();
        }
        public abstract void SetValue();
    }

    public class Sub : Base
    {
        public string value;
        public Sub()
        {
            value = "chentaihan";
        }

        public override void SetValue()
        {
            value = "陈太汉";
        }
    }

    public class Sub1 : Base
    {
        public string value = "chentaihan";

        public override void SetValue()
        {
            value = "陈太汉";
        }
    }
复制代码

 

如果执行下面这段代码会输出什么值呢,请不要往下看,先给出你自己的答案。

 

复制代码
 static class Program
    {
        static void Main(string[] args)
        {
            Sub sub = new Sub();
            Console.WriteLine(sub.value);
            Sub1 sub1 = new Sub1();
            Console.WriteLine(sub1.value);
            Console.Read();
        }
    }
复制代码

 

是的他很简单,但你确信你的答案就是对的吗?这么一个简单的问题我答错了,所以就有了这篇博客。 CLR VIA C#这本书告诉我们:成员在定义的时候初始化相当于在构造函数的最上面初始化,如果一个成员在定义的时候初始化,并在构造函数中赋值,那么在构造函数执行完成之后,该成员的值就是造函数中所赋的值,所以我得出的答案都是:chentaihan。但答案不是这样的。当运行结果出来时,我那个迷茫啊.....

 

先来说说我的简单分析:

1:进入子类构造函数

2Sub成员变量的内存被分配

3:调用父类构造函数

4:调用子类的方法SetValue(子类覆写了这个方法)value被赋值

5:正式执行子类构造函数,成员变量value再次被赋值

从上面5步我得出他们输出的结果一样,都是chentaihan。错在哪里呢?

于是我用Reflector查看了一下,得到的结果正如上面所说,他们的源码是一样的,如下所示。正如CLR VIA C#这本书说的那样,那为什么结果不一样呢,Reflector代码是一样的,执行的结果却不一样,怎么回事,怎么回事,那我只能说Reflector坑爹,它不能反映程序的真正执行逻辑,非要我用IL,我用的还不熟呢。

复制代码
   public class Sub : Base
    {
        public string value;
        public Sub()
        {
            value = "chentaihan";
        }

        public override void SetValue()
        {
            value = "陈太汉";
        }
    }
复制代码

 

神马情况,他们的IL代码是不一样的,如图所示

看了这个图,我们知道答案是chentaihan,陈太汉。谁能告诉我怎么调用父类的构造函数和给value赋值的顺序不一样啊。该用的工具都用了,我该怎么证明这个结果,于是开始单步调试,于是发现了一个每天都发现了的秘密:成员初始化在构造函数之前执行。难怪这本书上说成员在定义的时候初始化相当于在构造函数的最上面初始化,Reflector也证实了这个答案。但是又绕进另一个坑爹的问题:构造函数还没有调用,内存还没有分配,怎么给成员变量赋值啊?这不是问题,从上图可以看出成员变量的赋值只是在父类的构造函数之前调用,肯定也是在子类的成员变量分配空间之后为成员变量赋值。好的,最后我们得出的结论是: 

1:进入子类构造函数

2Sub成员变量的内存被分配

3Sub成员变量赋值

4:调用父类构造函数

5:调用子类的方法SetValue(子类覆写了这个方法)value被赋值

6:正式执行子类构造函数,成员变量value再次被赋值

同意以上观点的人请放过我,别吐槽,不同意的请留言

这样的解释答案就很合理,但同时也说明成员变量在定义的时候初始化和在构造函数中赋值的意义是不一样的,至少执行顺序不一样,产生的结果可能也不一样。

 

 

 

作者:陈太汉

博客:http://www.cnblogs.com/hlxs/

 



本文转自啊汉博客园博客,原文链接:http://www.cnblogs.com/hlxs/archive/2012/04/19/2456949.html

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
大三专科实习第一个月——推荐极具有价值的小众网站
脱变从现在开始,以下文章讲述的是广度问题接下来方向将是深度的问题。
25 0
Java8系列之 Lambda表达式(一)
Java8系列之 Lambda表达式(一)
8 0
Java 8系列之Lambda实践步骤(二)
Java 8系列之Lambda实践步骤(二)
5 0
Vue的方法的调用以及传参
前面的文章讲到了如何用CDN的方式引入vue.js。今天这篇文章介绍如何在vue中调用方法以及传参!
10 0
C#清除DataGridView数据DataGridView中的“无法删除未提交的新行”
C#清除DataGridView数据DataGridView中的“无法删除未提交的新行”
10 0
Vue3 的 Reactive 响应式到底是什么
​Vue 3 除了令人钦佩的性能改进,还带来了一些新功能。可以说,最重要的介绍是 `Composition API` 。在本文的第一部分中,我们将概括 Vue3 创建新 API 的动机:即,更好的组织和重用代码。在第二部分中,我们将重点讨论使用新API时较少讨论的方面,例如响应式特性。我将响应式特性其称为按需响应。
11 0
你知道学校里的MySQL与社会中的MySQL有啥区别吗?(详解一)
本文经验都是我看书学习的总结的一些经验,面试常问的知识点,所以请观看学习!下面已经给出了书的目录!今后将按目录的顺序继续更新学习心得!
5 0
最新!GitHub 推出 ReadME 项目,让世界听到开发者的声音
Coding 通常被视为一项单独的活动,但实际上,这是由一小群开源维护者、贡献者和团队领导的全球最大的社区活动。这些“无名英雄”常常需要花费大量的时间来构建软件、修复 issues、解答问题和管理社区,而往往没有得到资金资金或认可。开源项目的使用者与维护者的比例可以达到 2,000: 1 的极度不平衡。
5 0
最近这个 GitHub 项目,增长有点猛
今天给大家分享一个 GitHub 刷题项目,也是目前我们 Doocs 成员们在积极维护的一个项目。
4 0
+关注
1703
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
OceanBase 入门到实战教程
立即下载
阿里云图数据库GDB,加速开启“图智”未来.ppt
立即下载
实时数仓Hologres技术实战一本通2.0版(下)
立即下载