《改善C#代码的有效方法》读后感

一、C#语言的编程习惯

优先使用隐式类型的局部变量

1、开发者把更多注意力集中在名称上,而不用分心去考虑类型
2、编译器选取的类型可能比开发者指定的合适:如IQueryable和IEnumerable
3、变量是值类型,不建议var,可能产生以下问题
 (1)宽化转换,比较安全,比如float到double
 (2)窄化转换:会令精度下降,比如long到int

考虑用readonly代替const

1、const:编译器期量,使用它的地方,在编译时会变成a=1,若常量发生变更而不重新编译,会不生效;readonly:运行期常量,赋值更灵活
2、const性能比readonly好
3、const可能存在的问题:若A程序集引用了B程序集的常量,B程序集修改了常量的值,A程序集没有进行重新生成,则A程序的常量值还是B程序集的旧值

优先考虑is或as运算法,尽量少用强制类型转换

用内插字符串$取代string.Format()

1、C# 6.0 提供
2、可读性更好
3、避免序号与数组位置不对应或数量不相等导致的bug
4、不会创建参数化SQL查询,SQL语句需注意

使用nameof属性获取变量名的字符串

用nameof运算符来写代码的好处是:
如果属性名变了,那么用来构造PropertyChangedEventArgs对象的参数也随之变化。

使用委托表示回调

用null条件运算符调用事件处理程序

例如:线程安全调用 event?.Invoke();

尽量避免装箱和取消装箱这两种操作

1、装箱是将值类型转换成引用类型
2、取消装箱是把已经装箱的那个值拷贝出来
3、拆箱是将引用类型转换成值类型,只有装箱过的对象才能拆箱

只有在对应新版基类与现有子类之间的冲突时才应该使用new修饰符

不鼓励把基类所有方法都设置成虚方法

设置虚方法就是告诉使用者派生类可能以其他方式实现这个虚方法

二、.NET的资源管理

理解并善用.NET的资源管理机制

1、垃圾回收器GC
     (1)使开发者无需担心内存泄漏、迷途指针、未初始化的指针以及其他内存管理问题
     (2)非托管资源需要开发者自己控制,如数据库连接、GDI+对象、COM对象等
2、两种控制非托管资源机制
     (1)finalizer:防护机制,可以确保对象总是能够把非托管资源释放掉,但缺陷很多
     (2)IDsposable接口,推荐使用

用适当的方式初始化类中的静态成员

1、如果初始化开销比较大或复杂,可以考虑运用Lazy机制
2、如无多态,可在静态构造函数中实现单例

尽量减少重复的初始化逻辑

可以考虑带默认值的构造函数还是多个互相重载的构造函数

不要创建无谓的对象

绝对不要在构造函数里调用虚函数

实现标准的dispose模式

三、合理地运用泛型

这章节里的大多数篇幅比较常,代码量较多,就不多解释,有兴趣的可以去深入了解下。

通过运行期类型检查实现特性的泛型算法

通过IComparable及IComparer定义顺序关系

创建泛型类时,总是应该给实现了IDisposable的类型参数提供支持

考虑支持泛型协变和逆变

用委托要求类型参数必须提供某种方法

如果有泛型方法,就不要再创建针对基类或接口的重载版本

实现泛型接口的同时,还应该实现非泛型接口

如果不需要把类型参数所表示的对象设为实例字段,那么应该优先考虑泛型方法,而不是泛型类

只定义刚好够用的约束条件

只把必备的契约定义在接口中,把其他功能留给扩展方法去实现

考虑通过扩展方法增强已构造类型的功能

四、合理地运用LINQ

这章节里的大多数篇幅比较常,代码量较多,就不多解释,有兴趣的可以去深入了解下。

优先考虑提供迭代器方法,而不要返回集合

优先考虑通过查询语句来编写代码,而不要使用循环语句

我个人是觉得用循环可能好点,比较直观,一眼就知道代码是干嘛了,虽然查询语句也不差。
            // 使用循环
            var list1 = new List<int>();
            foreach (var item in Enumerable.Range(0, 100))
            {
                list1.Add(item * item);

            }
            foreach (var item in list1)
            {
                Console.WriteLine(item);
            }
            // 使用查询语句
            var list2 = (from n in Enumerable.Range(0, 100)
                         select n * n).ToList();
            list2.ForEach(n => Console.WriteLine(n));

把针对序列的API设计得更加易拼接

将迭代逻辑与操作、谓词及函数解耦

等真正用到序列中的元素时再去生成

考虑通过函数参数来放松耦合关系

有点像AOP切面编程

绝对不要重载扩展方法

理解查询表达式与方法调用之间的映射关系

尽量采用惰性求职的方式来查询,而不是及早求值

考虑用lambda表达式来代替方法

不要在Func与Action中抛出异常

掌握尽早执行与延迟执行之间的区别

只有当程序确实要用到某个方法的执行结果时,才会去调用这个方法。
这就是声明式写法和命令式写法的重要区别。

不要把开销较大的资源捕获到闭包中

注意IEnumerable与IQueryable形成的数据源之间的区别

用Single()及First()来明确地验证你对查询结果所做的假设

不要修改绑定变量

五、合理地运用异常

令代码在发生异常时依然能保持稳定是每一位C#程序员所应掌握的关键技能。

考虑在方法约定遭到违背时抛出异常

1、如果方法不能够完成其所宣称的操作,那么应该通过异常来指出这个错误,如果改用错误码来实现,那么这些代码,容易被调用方所忽视。
2、错误码必须由调用方来处理,而异常则可以验证调用栈向上传播,直至到合适的catch子句。

利用using与try/finally来清理资源

如果某个类型用到了非托管的系统资源,如File,那么就需要通过IDsposable接口的Dispose方法来明确的释放。
而想要确保方法总是能够得到调用,最好的办法就是利用using语句或者try/finally语句块。

专门针对应用程序创建异常

优先考虑做出强异常保证

优先用异常筛选器来改写先捕获异常再重新抛出的逻辑

采用异常筛选器会给程序性能带来正面影响。.NET CLR对带有when关键字的try/catch结构做了优化,
使得程序在无须进入该结构时其性能尽量不会受到影响。

合理利用异常筛选器的副作用来实现某些效果

如下,捕捉异常后,保存日志后继续抛出:
		// SaveLog返回false
		// 不使用筛选器
              try
                {
                    try
                    {
                        Convert.ToInt32("w");
                    }
                    catch (Exception ex)
                    {
                        SaveLog(ex);
                        throw;
                    }
                }
                catch { }
         // 使用筛选器
                try
                {
                    try
                    {
                        Convert.ToInt32("w");
                    }
                    catch (Exception ex) when (SaveLog(ex))
                    {

                    }
                }
                catch {  }

你可能感兴趣的:(代码质量,C#及.NET)