Item12: Prefer Variable Initializers to Assignment Statements
一般来说一个类会有不止一个构造函数,有的时候会因此而造成成员变量和构造函数的脱节。解决这种问题的最好方法就是当变量声明时就对其进行初始化,而不是初始化工作放到构造函数中。
我们应当在声明成员变量的时候就对其进行初始化。




不论在MyClass类型中的有多少个构造函数,_coll都会被初始化。编译器在生成类的构造函数之前就会先执行我们为这些成员变量定义的初始化工作。当我们添加一个新的构造函数时,即便什么也不做,_coll还是得到了适当的初始化。这样做的另一个好处就在于当我们需要添加一个新的成员变量时,我们不必在每一个构造函数中添加其初始化代码。非常重要的是,我们这些初始化工作也会被编译器添加到默认的构造函数中去。即便我们没有为我们的类型定义任何构造函数,编译器也会为我们生成默认的构造函数。
使用初始化声明是很简洁的。这些变量初始化的声明会放到我们的类型的构造函数之前。它们会在基类构造函数之前被运行。而它们初始化的顺序则是根据我们的声明顺序而来。
在定义时就使用初始化声明成员变量是避免出现未初始化成员的好方法,但这不是完美的。有三种情况下我们不应当在定义变量的时候就使用初始化声明。
第一种情况是在我们要声明成员对象为“0”或null时。在我们的构造函数运行之前,系统默认的初始化会为我们所有的成员变量设为“0”值。这种设“0”操作是通过直接对内存操作而得到的。在这之后,任何额外的设“0”操作都是多余的,然而编译器并不知情,这种额外的操作还会进行一次。这样做是没有意义的,只会降低程序的效率,特别是对于值类型来说。


上例中两个声明的变量都是“0”。第一个是通过对内存进行设置而得到“0”值,第二个是通过IL设置initobj来得到的。第二种方法会涉及boxing和unboxing操作,这会浪费额外的时间。
第二种情况是当我们为同一个成员变量创建了多个不同的初始化时。只有在所有构造函数中声明的变量相同时,我们才能使用初始化来声明对象。下面的MyClass类中的两种构造函数声明了两种不同的ArrayList:












当我们通过第二个构造函数创建一个新的MyClass时,我们实际上创建了两个ArrayList。其中一个马上被废弃了,因为变量的初始化是先于类构造函数执行的。类构造函数又创建了第二个ArrayList。对于编译器来说,我们的代码就好想是下面这样:














最后一种让我们将初始化工作放入构造函数中的原因是它便于异常处理。如果我们直接在声明时初始化,那么我们就不能将其放入try程序块中。如果我们要初始化的成员变量容易产生异常,那么我们应当将它放到构造函数中进行初始化,这将使得我们更容易捕获创建时产生的异常。
在声明时初始化成员变量是一种确保成员变量没有在构造函数中被遗漏初始化的简单方法。它会在构造函数运行之前被执行。当我们为类型使用了新的成员变量时,我们也不会因为忘记在构造函数中添加适当的初始化而犯错。在所有构造函数创建的成员变量相同的情况下,我们应当尽量使用即容易理解又容易维护的声明时初始化的方法。
译自 Effective C#:50 Specific Ways to Improve Your C# Bill Wagner著
回到目录
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通