• 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.

     

  • 相关阅读:
    GTK+ 3.6.2 发布,小的 bug 修复版本
    RunJS 新增 Echo Ajax 测试功能
    Mozilla 发布 Popcorn Maker,在线创作视频
    Sina微博OAuth2框架解密
    Mina状态机State Machine
    Mozilla 发布 Shumway —— 纯JS的SWF解析器
    Code Browser 4.5 发布,代码浏览器
    ROSA 2012 "Enterprise Linux Server" 发布
    ltrace 0.7.0 发布,程序调试工具
    Artifactory 2.6.5 发布,Maven 扩展工具
  • 原文地址:https://www.cnblogs.com/zhangronghua/p/ReadingNotesForEffectiveCSharp2nd.html
Copyright © 2020-2023  润新知