《Effective C#》快速笔记(三)

时间:2022-01-06 02:54:16

《Effective C#》快速笔记(三)

 目录

二十一、限制类型的可见性

二十二、通过定义并实现接口替代继承

二十三、理解接口方法和虚方法的区别

二十四、用委托实现回调

二十五、用事件模式实现通知

二十六、避免返回对内部类对象的引用

二十七、让类型支持序列化

二十八、提供组粒度的因特网服务 API

二十九、支持泛型协变和逆变

二十一、限制类型的可见性

  1.在保证类型可以完成工作的前提下,应该尽可能地给类型分配最小的可见性。

  2.我们经常下意识的创建公有类型。可见性越低,以后升级或更改时所需要的变化就越小,,因为能访问你功能模块的代码越少。

  3.创建内部类是一种常被忽略限制类型作用域的做法,我们经常习惯不假思索地创建公有类。你应该仔细思考这个一个类型的作用范围,即它是将被所有的客户使用,还是仅用在这个程序集的内部。

  4.更少的公有类型可以减少单元测试的数量。

  5.以公有形式暴露给外界的类和接口将成为你的组件的契约。接口越冗余,日后的修改就越受限。暴露的公有类型越少,以后更新扩展的时候周旋的余地就会越大。

二十二、通过定义并实现接口替代继承

  1.抽象基类为类的继承体系提供了一个共用的祖先,接口描述了一组原子性的功能。接口是一种契约,抽象基类则为一组相关的类型提供了一个共用的抽象。基类描述了对象是什么,接口描述了对象如何表现它的行为。

  2.我们可以将可重用的行为提取出来,定义在接口中。由于不相关的类型均可以实现一个接口,这表示代码的重用率将大大增加。

  3.如果向基类中添加一个方法,所有派生类都将自动包含该方法。也就是说,随着时间的推移,仍可以有效扩展多个类型功能的途径。通过向基类添加并实现某种功能,所有的派生类都将立即拥有该功能。而向接口中添加一个成员,会破坏所有实现该接口的类。因为这些类不包含新方法,每一个实现都需要进行更新,然后重新编译。

  4.在抽象基类和接口之间做选择,实际上就表示了对日后可能发生变化的不同处理态度。接口是固定的:我们将一组功能封装在一个接口,作为其他类型的契约。而基类则可以在日后扩展,这些扩展也会成为每个派生类的一部分。

  5.也可以使用扩展方法进行扩展。

public static class Extendsions { public static void ForAll<T>(this IEnumerable<T> sequence, Action<T> action) { foreach (var item in sequence) { action(item); } } }

  方法调用:

IEnumerable<object> objects = new List<string>(); objects.ForAll(x => Console.WriteLine(x)); objects.ForAll(Console.WriteLine);

  6.有时候,使用接口可以帮助我们避免 struct 类型的拆箱所带来的代价。

二十三、理解接口方法和虚方法的区别

  1.在基类中实现一个接口时,派生类需要使用 new 来隐藏对基类方法的使用。

  2.可以将基类接口的方法申明为虚方法,然后在派生类中实现。

二十四、用委托实现回调

  1.类之间需要通信时,并且我们期望一种比接口所提供的更为松散的耦合机制时,委托就是最佳的选择。

  2.多播委托会把所有添加到该委托中的目标函数组合成一个单一的调用。需要注意的是:

  (1)如果有委托调用出现异常,那么就不能保证安全;

  (2)整个调用的返回值将为最后一个函数调用的返回值。

  3.在多播委托调用的过程中,每个目标会被依次调用。委托对象本身不会捕捉任何异常。因此,任何目标抛出的异常都会结束委托链的调用。

二十五、用事件模式实现通知

  1..NET 中的事件就是观察者模式的一个语法上的快捷实现。

  2.使用 System.ComponentModel.EventHandlerList 容器来存储各个事件处理器,在类型中包含大量事件时可以使用他来隐藏所有事件的复杂性。

  3.EventHandlerList并没有提供内建的泛型实现,可以自行基于 Dictionary 构建。

二十六、避免返回对内部类对象的引用

  1.4 种策略可以防止类型的内部数据结构遭受有意或无意的修改:值类型、常量类型、接口和包装器(wrapper):

  (1)当客户代码通过属性来访问值类型成员时,实际返回的是值类型的副本。

  (2)常量类型,如:System.String 也是安全的。