Effective C# 2nd

1 尽量用Property而不是用Public Field,因为Property更符合OO的封装性,更可控。

2 尽量用ReadOnly,而不是用ConstReadOnly虽说有稍微的性能损失,但更灵活,不会出现发布新版本时必须要求调用者也重新编译的问题。

3 尽量用as/is来做类型转换而不是用Cast的方式,as/is可以避免类型转换中产生的运行时异常。

4 使用Condition Attribute来代替#if/endif

5 在创建类时总是重写ToString()方法,Object.ToString()只返回TypeName是不合适。

6 理解多个Equalsy方法的区别
Object.ReferenceEquals比较引用地址相同。
Object.Equals(object a, object b)比较两个只有你在运行时才能确定的类型的相等性

View Code
public static new bool Equals(object left, object right)
{
//check object identity
if(Object.ReferenceEquals(left, right))
{
return ture;
}

// both null reference handled above
if(Object.ReferenceEquals(left, null) || Object.ReferenceEquals(right, null))
{
return false;
}

return  left.Equals(right);
}

Equals(object right) 方法
Object中类似于ReferenceEquals
ValueType中是利用反射来比较所有的字段,效率比较低。所以在定义ValueType时要Override Equals(object)方法,
ReferenceType.Equals(object right)等同于ReferenceEquals(),不过String DataRow等做了Overrite,比较内容,而不是比较Reference.
operator==()类似于Equals(object),同样的原因我们要对ValueType进行重写。
综上,ReferenceEqualsStatic Equals永不需重写,EqualsOperator==()对于Value Type需要重写,重写时记得同时重写GetHashCode方法

7 GetHashCode()
ReferenceTypeGetHashCode是在构造函数里生成一个整数,来GetHashCode,working but inefficient,ValueType.GetHashCode取的第一    个MemberHashCode,所以只有当frist memberreadonly or immutable时才正确

8 优先使用查询语法(Query Syntex)而不是优化使用循环(Loops),Query Syntex语义更清楚,更有可读性。

9 API中避免给类型增加类型转换功能(implicit and explicit),因为类型转换带来的类可置换性可能会引起一些不必要的错误。要想提供类的转换时,使用构造函数。

10 利用Optional ParameterNamed Parameter来减少重载的次数,并增加方法的语义性。

11 多重构,多写小方法,而不是写一个大的方法,小方法可读性好,可利用JIT来做Inline Compiler. 关于InlineCompiler有一点需要注意就算方法很小,很清晰,但只要方法内有try/catch或者方法是virtual的就不会被Inline Compiler.

12 要理解GCGC负责Management Resource管理,Unmanagement的还是需要自己管理。GCGeneration0,1,2.每次GC在单独的进程里清理资源时如果
对象没有Finalizer方法则直接清理,如果有Finalizer则将对象放到下一个Generation并在一个单独的进程里运行对象的Finalizer。另外每次的GC清理时GC还压缩Heap的空间,以便新分配的对象可以得到连续的空间。GC的执行时间是不确定的,G0执行最频繁,G1次之,G2执行最少。

13 在声明变量的时候就初始化。有几种情况需要避免,声明变量的值为0或是null时,因为C# CLR会用low levelcpu指令来做这些事,会比我们做的更高效;优先使用自动属性,自动属性可读性好,JIT编译时效率也更高;需要做异常处理的初始化。

14 静态变量的初始化,先在定义时初始化,如果逻辑复杂或是需要异常处理,则定义static constructor.

15 如果需要多个构造函数,那么声明一个公用带参数的构造函数,然后在其它构造函数里调用公用的构造函数,不要在多个构造函数里重复代码。如果是.NET4.0的话,也可以考虑用参数默认值来完成相同的操作。

16 利用using或是try/finally来释放Unmanaged resource.

17 避免声明不必要的对象,尤其是heap based object.

18 理解DisposeFinalizer的区别,前者由调用方直接显示调用,负责释放Managed&Unmanaged Resource,后者由GC自动调用,只负责释放Unmanaged Resource.     

View Code
public class Foo: IDisposable
{
private bool disposed = false;

public void Dispose()
{
dispose(true);
GC.SuppressFinalize(this);
}

protecte virtual void dispose(bool disposing)
{
if(!disposed)
{
if(displosing)
{
// elided. to release Managed resource.
}

// elided. to release Unmanaged resource.

disposed = true;
}
}

~Foo()
{
dispose(false);
}
}

19 理解Value Type and Reference Type的区别,Value Type用来定义数据,尽量不包括行为,Value Type并不老是分配在Stack上,例如类的Field就是跟着类分配在Heap; Reference Typee用来定义数据,可以包括行为,总是在Stack上分配一个引用,然后在Heap上分配具体的内容。

20 在创建Value Type时确保0是有效的值,比如在创建enum时,第一个值总是0,要不可能会引用运行时的异常或者使用不方便,因为默认的构造函数是将enum的实例赋值为0

21 Prefer immutable atomic data types.

22 总是暴露最少的成员,给成员以最小的可见性,比如能internal的尽量不要public.

23 优先使用 interface 而不是用 virtual class.

24 要理解virtual/abstruct/interface的区别。 interface定义一组协议,可以被一组类显式或是隐式实现,interface的成员全是public的,并且无构造函数。abstruct实现了子类必须要实现的功能,必须在抽象类中定义,抽象类不能实例化。virtual定义子类的功能,可以提供实现,子类可以override也可以不override. 感觉interface是面向一组类的,
这组类可以不在一个继承链上,virtual/abstruct都是baseclassDerivedClass的要求。

25 了解delegate/event的作用及区别,event是一个特殊的delegate,比delegate多了限制,当你声明一个Public eventCLR会类似于对待自动属性一样给event添加add/remove方法,除了这两个方法不能再对event所代表的delegate做更多的操作,为的是防止调用类破坏eventmulticast chain.所以event一般用在观察者模式类似的场景上,delegate的应用场景要广。

26 Interal Class Objects要避免返回引用类型的参数

27 尽量使自定义的类型为可序列化的。

28 在进行Server/Client的通信时要精心设计API,减少通信的次数以及每次通信传输的数据集大小。

29 Covariant(协变) Contravariant(逆变)是为了弥补在4.0之前的范型是invariant的缺点而在4.0引入的。
如果一个可变性和子类到父类转换的方向一样,就称作协变;而如果和子类到父类的转换方向相反,就叫反变性。
Out来描述仅能作为返回值的类型参数,用In来描述仅能作为方法参数的类型参数。
由子类向父类方向转变是协变协变用于返回值类型用out关键字
由父类向子类方向转变是逆变逆变用于方法的参数类型用in关键字

30 在给类添加事件时,优先使用重与的方法,而不是优先使用+=的方法。
protected override void OnMouseDown(MouseButtonEventArgs e)//优先使用
this.MouseDown += OnMouseDown //优先级低。
重写的方式更高效,也更清晰一些,不过声明式的书写因为可以在前台文件(如aspx,xmal)中书写,所以方便分工合作时desinger来书写。并且因为可以在运行时改变也更灵活一些。所以我个人还是喜欢声明式的方法多些。

31 当类型有可能会被排序时,让类型同进继承IComparableIComparable<T>接口,实现IComparable是为了兼容以前的代码,IComparable的方法需要用到unboxbox所以效率不高。

32 Once a type supports ICloneable, all it's derived types must do the same. all it's member tpes must also support ICloneable or have some other mechanism to create a copy.
所以实现ICloneable接口很麻烦,如非必要,尽量不要实现它。
shallow copy & deep copy.

33 new关键字可以隐藏父类的方法,可以并不代表我们需要,一般来说new只适用用你的代码已经分发出去有很多的调用方,并且你对调用方的代码没有修改权限,这时在未来的某个时候你的代码的基类新增了一个和你同名的方法,
这时可以考虑用new隐藏掉基类新增加的谅地,不过还是尽量避免使用new关键字吧,因为同一个方法名实际上却是两个不同的方法,太容易让人引起歧义了。

34 尽量避免Overload在父类中定义的方法,否则可能会引起难发现的bug.

35 了解PLinq相关的知识
partition分为四种算法:
Range partitioning 最简单,item平均分配到每个cpu资源
Chunk partitioning 在任何可能的时间给请求的资源更多的item.
Strip partitioning Range的变得,可以按135 246这样的方式给两个cpu分配item.
Hash partitioning 为有join, groupjoin,groupbydistinct,exceptinion操作的表达式准备的,
执行时有三种算法:
Pipelining 各个cpu顺序执行
Stop&Go 当对表达式执行ToList/ToArray时会用这种方式
Inverted Enumeration 添加一些操作在每个项的结果上。
var nums = from n in Enumerable.Range11000).AsParallel
select nn;
nums.ForAll(item => Console.WriteLine(item))
Linq2Objects是只编译,当要用到每一项时才长每一项,PLinqLinq2Sql一样执行第一个时顺带把全部的结果一块执行出来。

PLinq还是适合项可以并行执行,对顺序没有要求的集合。

36 Dynamic can be thought of as "System.Object with runtime binding"
left the safety of the type system behind所有的错误只能在运行时发现。
Dynamic应该只用在我们在编译时不知道类型信息的下,如果我们在运行时知道类型信息,那么完全可以用范型搭配Lambda表达式来完成。

37 Select().Cast() cannot access any user-defined conversions on the runtime type of its argument.
the only conversions it can make are reference conversions and boxing conversions.
Cast<T> cannot access any user-defined conversions because it can only assume that T contains the members
defined in System.Object. System.Object does not contain any user-defined conversions.
所以当要Cast的类型有自定义的类型转换时,我们应该用.NET4中新增的Convert<T>方法,这个方法的内部实现中调用了类型转换,
所以可以调用类型自定义的类型转换,不会出现运行时的异常。

38 one of the shortcoming of anonymous types has been that you cannot easily write method
using them as parameters or return types.
但是如果你的方法换成dynamic的参数或是返回值的话就没有这个限制了。

39 尽量多用范型来避免产生box and unbox操作。

40 尽量要求最少的权限,对于一些unmanaged resources如果不是必需,则不申请,否则当程序集通过internet分发时会遇到一些在开发时遇不到的
权限问题。

41 Perfer CLS(Common Language Subsystem)-Compliant Assemblies.
[assembly: System.CLSComplianta(true)]
42 Prefer Smaller, Cohensive Assemblies.

 

原文地址:https://www.cnblogs.com/zhangronghua/p/ReadingNotesForEffectiveCSharp2nd.html