最近在读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更好地决定。