标签:
续 《Effective C#》读书笔记(一)- C# 语言习惯。
.NET 中,GC 会帮助我们管理内存,我们并不需要去担心内存泄漏,资源分配和指针初始化等问题。不过,它也并非万能,因为非托管资源需要我们自己进行清理,如文件句柄、数据库连接、GDI+ 对象和COM 对象等。
目录十二、推荐使用成员初始化器而不是赋值语句
十三、正确地初始化静态成员变量
十四、尽量减少重复的初始化逻辑
十五、使用 using 和 try/finally 清理资源
十六、避免创建非必要的对象
十七、实现标准的销毁模式
十八、区分值类型和引用类型
十九、保证 0 为值类型的有效状态
二十、保证值类型的常量性和原子性
十二、推荐使用成员初始化器而不是赋值语句1.成员初始化器:在声明变量时就进行初始化,而不是在每个构造函数中进行。
2.以下 3 种情况,应避免使用成员初始化器:
(1)当你想要初始化对象为 0 或 null 时。因为系统默认的初始化工作(在所有代码执行前)会将一切设置为 0 或 null,我们做的是一步多余的操作。而且,如果是值类型,那么性能非常差。
MyValueType myVal1; //初始化为 0 MyValueType myVal2 = new MyValueType(); //也是 0
这两条语句都将变量初始化为 0,但第一条是通过设置包含 myVal1 的这一块内存为 0 实现的,而第二条使用的是 initobj 这条 IL 指令,导致了对 myVal2 变量的一次装拆箱操作,这将占用额外性能与时间。
(2)需要对同一个变量执行不同的初始化方式:
class Program { /// <summary> /// 声明并初始化 /// </summary> private List<string> _lables = new List<string>(); public Program() { } public Program(int capacity) { _lables = new List<string>(capacity); } }
初始化该类时,假如使用的是带 capacity 的构造函数,那么 List<string> 对象表示初始化了 2 次,头一个就成为了垃圾对象。
(3)将初始化代码放在构造函数的合适理由:可以方便异常管理 try-catch。
十三、正确地初始化静态成员变量1.在使用类型的实例之前,就应该初始化该类型的所有静态成员变量。静态构造函数是一个特殊的函数,将在其他所有方法、变量或属性被第一次访问之前执行。你可以使用这个函数来初始化静态变量和实现单例模式等操作。
2.静态初始化器和静态构造函数是初始化类的静态成员的最佳选择。
3.使用静态构造函数而不是静态初始化器最常见的理由是可以捕捉和处理异常。
十四、尽量减少重复的初始化逻辑1.如果多个构造函数包含类似的逻辑,我们应将其提取到一个公共的构造函数中,这样可以避免代码重复,也可以利用构造函数初始化器生成更高效的代码。
class MyClass { private List<string> _lables; private string _name; public MyClass() : this(0, string.Empty) { } public MyClass(int capacity = 0, string name = "") { _lables = new List<string>(capacity); _name = name; } }
第二个构造函数使用了 "" 来给出 name 的默认值,而不是采用 string.Empty ,因为 string.Empty 并不是一个编译期的常量,而是一个定义在 string 类中的静态属性,所以不能用作参数的默认值。
2.创建某个类型的第一个实例时所进行的操作顺序:
(1)静态变量设置为 0 ;
(2)执行静态变量初始化器;
(3)执行基类的静态构造函数;
(4)执行静态构造函数;
(5)实例变量设置为 0;
(6)执行实例变量初始化器;
(7)执行基类中合适的实例构造函数;
(8)执行实例构造函数。