Effective C#(一)

最近在读Effective C#这本书。写的简直太TM好了。反正读了很有收获啦,强力推荐。为了防止狗熊掰棒子,就写一下读书笔记吧。自己感觉还是比网上的一些写的全的。但能不能坚持下去就不好说啦~~所以,且看且珍惜哦~

直接从Evernote复制过来的~凑合看哈~

    • Use Properties Instead of Accessible Data Members
      • 属性会被编译器编译成方法,所以支持一些成员变量不支持的特性,例如数据绑定、多线程操作。还可以将属性设为abstract或virtual。另外属性里还可以进行数据验证操作。
      • 在属性内部的操作中,不应当写过度复杂或消耗性能的操作。
      • 可以实现索引器属性。写法是public object this[object index]。属性名必须是this。也可以实现多个索引,例如public Vector2 this[float x, float y]。
      • 属性的调用并不会比直接的数据变量调用多消耗多少性能。
    • Prefer readonly to const
      • readonly是运行时常量,而const是编译时常量。
      • readonly 可以是任意类型,而const只能是基本数据类型(int, float, string, bool)。
      • readonly是取指定数据,而const是直接将数据值进行替换。因此const有一定的性能的优势。但比readonly灵活性差。
      • const隐含指定了是静态的。
      • 在主程序集引用另一个程序集的const时,如果这个const的值有了改动,并替换了程序集,主程序集中的值并不会有变化。除非重新编译主程序集。所以还是用readonly更安全啊~
      • 可以使用const来记录版本号。因为它只在编译时有改动。
    • Prefer the is or as to casts
      • is或as仅仅进行类型判断,如果判断失败,返回null。 而进行cast时,如果没有用户自定义的转换,则会编译报错。而如果运行时无法转换,则会抛出异常。所以进行cast时,往往需要使用try...catch。
      • as不能使用在值类型变量上。所以使用前需要用is判断。
    • Use Conditional Attribute Instead Of #if
      • 过多使用#if和#endif会使代码结构混乱,难以维护。而且留下空的方法体也会造成额外的开销。
      • Conditional Attribute只能使用在方法体上,而且该方法必须返回值为void。
      • 有Conditional Attribute的方法,在条件不满足时,不会被调用。参数中的表达式也不会执行。
    • Always Provide ToString()
      • 对每个类都重写ToString()方法。默认的ToString()的返回值没有什么意义。
    • Understand the relationships among the many different concepts of equality.
      • C#有4种等于判断方法:static bool RefereceEquals(object left, object right), static bool Equals(object left, object right), virtual bool Equals(object right), static bool operator==(MyClass left, MyClass right)。永远不要重写前两个方法。
      • RefereceEquals()判断两个变量是否引用相同的对象。所以,当使用值类型作为参数时,永远返回false。
      • Object.Equals()判断两个未知类型的变量是否相等。
      • ValueType.Equals()会使用反射进行等于判断。所以最好重载自定义的ValueType.Equals()。
      • Equals()实例方法只有在需要进行内容判断而不是引用判断时才有必要。而且要遵从固定的格式,实现IEquatable<T>接口。
        • 永远不要抛出异常,因为没意义。。。
        • public class Foo : IEquatable<Foo>
          {
               public override bool Equals(object right)
               {
                    // check null:
                    // this pointer is never null in C# methods.
                    if (object.ReferenceEquals(right, null))
                         return false;
                    if (object.ReferenceEquals(this, right))
                         return true;
                    // Discussed below.
                    if (this.GetType() != right.GetType())
                         return false;
                    // Compare this type's contents here:
                         return this.Equals(right as Foo);
               }
               #region IEquatable<Foo> Members
               public bool Equals(Foo other)
               {
                    // elided.
                    return true;
               }
               #endregion
          }
      • 当定义自己的值类型时,需要自定义operator==(),因为值类型的等于判断会默认使用反射。
    • Understande the pitfall of GetHashCode()
      • GetHashCode()只在哈希容器的key值中使用。无论值类型还是引用类型的对象,都没有既准确又高效的实现。
      • 所有对GetHashCode()的重载都必须遵从以下原则:
        • 两个相等的对象(根据operator==()定义)必须返回相同的哈希值。
        • 无论对对象有怎样的操作,GetHashCode()都返回相同的哈希值。
        • 根据输入得到的哈希值应当平均地分布在整个整数值的值域内。
      • 每一个对象都有一个字段记录对象编号。在构造函数中赋值,并且不能更改。Object.GetHashColde()就是返回这个值。
      • ValueType.GetHashCode()返回第一个字段的GetHashCode()。如果第一个字段不是不可变的,这会带来隐患!
    • Prefer query syntax to loops
      • 查询标记比循环更易懂易维护。循环更注重的是操作的行为,而不是程序设计的意图。
    • Avoid conversion operator in your APIs
      • 类型转换操作符可能带来隐患。因为创造的对象是临时的。
        static public implicit operator Ellipse(Circle c)
        {
             return new Ellipse(c.center, c.center, c.radius, c.radius);
        }
        你对这个创建的Ellipse对象的所有操作都没有意义,因为这个对象只是临时的。例如下面这个方法:
        public static void Flatten(Ellipse e)
        {
             e.R1 /= 2;
             e.R2 *= 2;
        }
        调用这个方法后,你的Circle半径并不会有改变。
    • Use optional parameters to minimize your  overloads
      • 命名参数可以给指定的含默认值的参数赋值。一个方法如果有100个含默认值的参数,而你要给最后一个参数赋值,使用命名参数就可以避免把前面99个都写一遍了。
      • 而且,使用命名参数可以大大减少重载的工作量,但能带来一样的灵活度。
      • 使用命名参数可以避免相同类型的参数因为顺序导致的错误。例如下面这段代码:
        private void SetName(string lastName, string firstName)
        {
        }
        SetName(lastName: "Wagner", firstName: "Bill");
      • 但是命名参数有隐患。当给参数重命名时,会导致方法调用失效。需要手动改。
    • Understand the attraction of small functions
      • C#代码会被编译成IL的Assembly,而Assembly会在运行时将将要执行的方法编译成对应平台的机器码。
      • 由于方法只有被执行时才被编译,所以写一些小的方法会带来性能的提升。
      • 而且小的方法(方法体里面不能有try...catch或者是virtual)可以被内联。是否内联由JIT决定。
      • 使用频率较高的局部变量有可能被放置到寄存器进行优化。因此使用更少的局部变量,可以让JIT更好地决定。

 

原文地址:https://www.cnblogs.com/sigmadruid/p/4817052.html