C#代码优化
参考书籍:《Effective C#》
C# 托管资源和非托管资源:https://blog.csdn.net/zlwzlwzlw/article/details/7918633
-
优先使用隐式类型的局部变量:即用
var
声明局部变量。 -
考虑用
readonly
代替const
。 -
优先用
is
或as
运算符,少用强转类型。- 用
as
把装箱的值类型转换成未装箱且可以为null的值类型,则会创建新的对象。 - 判断数值类型:
var i = o as int?
,即判断int?
可空类型是否为null。
- 用
-
使用内插字符串代替
string.format()
:Debug.Log($"圆周率:{Math.PI.ToString()}")
- 内插字符串生成的代码会调用一个
params
对象数组的格式化方法,所以会转成字符串,如果是数值类型,那就会有装箱操作,应该先自己转成字符串。
- 内插字符串生成的代码会调用一个
-
使用
nameof()
方法来获取变量名称。 -
用委托表示回调:
List<int> numbers = Enumerable.Range(1, 200).ToList(); var oddNumbers = numbers.Find(n => n % 2 == 1); var test = numbers.TrueForAll(n => n < 50); numbers.RemoveAll(n => n % 2 == 0); numbers.ForEach(item => Console.WriteLine(item));
-
用null条件运算符调用事件处理程序,可避免被线程打断的危险操作。
// 1.不安全 if (Callback != null) // 判断完后,别的线程打断,把Callback置为Null Callback(this, abc); // 这时候Callback为Null,异常 // 2.安全但冗长 var handle = Callback; // 浅拷贝 if (handle != null) handle(this, abc); // 3.推荐 Callback?.Invoke(this, abc);// 运算符左侧只计算一次
-
避免装箱拆箱。
-
C#的GC:每次遍历对象都记一代,代数越高,检查频率越低。
- 实现并运用
IDisposeable
接口,以便在不给GC增加负担的前提下把资源清理干净。
- 实现并运用
-
声明字段的时候最好直接初始化。
-
用静态构造函数初始化,捕获异常在构造函数内,如果不是,CLR会抛出
TypeInitializationException
以终止程序。 -
减少重复初始化逻辑。
class AAA { public MyClass() : this(0, "aaa") {} public MyClass(int i = 0,string str = "") { ... } }
-
构建某个类型的首个实例时系统所执行的操作:
- 把存放静态变量的空间清零。
- 执行静态变量的初始化语句。
- 执行基类的静态构造函数。
- 执行(本类的)静态构造函数。
- 把存放实例变量的空间清零。
- 执行实例变量的初始化语句。
- 适当地执行基类的实例构造函数。
- 执行(本类的)实力构造函数。
-
频繁调用的局部引用类型的对象应该提升为成员变量。
-
绝对不要在构造函数中调用虚函数,因为可能基类会调用到重写后的虚函数,造成错误。
-
实现
IDispoable.Dispose()
方法时,要注意:- 把非托管资源全部都释放掉。
- 把托管资源全部释放掉(不订阅的事件)。
- 设定标识说明该对象已经清理过了,如果有人要访问成员,可以抛一个异常
ObjectDisposedException
。 - 阻止垃圾回收器重复请离开该对象。
GC.SuppressFinalize(this)
。
-
泛型:
- 封闭式泛型:所有类型参数都已经指明。
- 开放式泛型:仅指出了某些参数。
- 如果泛型类的类型是引用类型,那么无论具体指的是什么,JIT编译器都会生成同样的机器码。
- 如果泛型类的类型是值类型,JIT编译器会根据不同的类型参数生成对应版本的机器指令。
-
泛型最好只定义刚好够用的约束条件。
-
通过
IComparable<T>
及IComparer<T>
定义顺序关系。 -
如果不需要把类型参数所表示的对象设为实力字段,那么应该优先考虑创建泛型方法,而不是泛型类。编译器可以只针对调用泛型方法的那些参数来生成代码,而不是整个类都生成。
-
不要在
Func
与Action
中抛异常,这样遍历的时候,无法确定异常位置。 -
在方法阅读遭到违背时抛出异常。例如
File.Open()
在文件不存在的时候会抛异常,因为文件要求存在。 -
利用
using
与try/finally
来清理资源。编译类型支持IDisposable
接口的才能用在using语句中。