翻譯自Effective C#
1、屬性(properties)
抽象屬性可以放在接口的定義裡。如:
public interface INameValuePair
{
string Name { get; }
T Value{ get; set; }
}
所有私有字段的訪問都建議使用屬性來處理,類中所有的數據成員都應當是私有的。
屬性在FCL內部其實就是一個方法。
屬性也可以是虛擬的(virtual),針對單獨的get或set訪問器可以有單獨的訪問修飾符。如:
public virtual string Name
{
get;
protected set;
}
JIT編譯時使屬性稱為內聯方法,所以速度和普通數據成員幾乎一樣.
屬性裡不要做冗長的計算或者跨應用的訪問(如查詢數據庫).
2、偏愛readonly而不是const
C#有兩種常量類型:編譯時常量(用const聲明)與運行時常量(用readonly聲明)。
編譯時常量可以在方法內聲明,而運行時常量不可以。
編譯時常量只能是原子類型(內置整型、浮點型、枚舉、string等),而運行時常量可以是任何類型。
最重要的是Readonly類型變量在運行時被解釋,IL會生成對應對象的常引用,而const變量會被IL生成對應的值,而這樣會對後期的維護造成不便。如下代碼:
public class UsefulValues
{
public static readonly int StartValue = 5;
public const int EndValue = 10;
}
而在另一程序集,你引用了這些值:
for(int i = UsefulValues.StartValue;i
然後經過一段事件你對原程序集進行了修改,修改後:
public class UsefulValues
{
public static readonly int StartValue = 105;
public const int EndValue = 120;
}
此時你希望輸出是
value is 105
value is 106
...
value is 119
而實際將什麼都不輸出,因為此時的for循環已經是如下代碼:
for(int i = UsefulValues.StartValue;i<10; i++)
Console.WriteLine("value is {0}", i);
const變量會比readonly變量要稍微快一點,因為IL是直接為其生成常量字符串或數字,而readonly的靈活性要更好,因此我們應當偏愛readonly.
3.偏愛is和as而不是Cast
請使用as來進行類型轉換,因為它更安全、更有效。
as與is操作符不能執行任何的用戶自定義類型轉換,它從不自己構造新對象。而Cast則可實現類型轉換,它會轉換一個對象為請求的類型,如果轉換一個高精度類型到低精度類型,則可能會丟失信息。
注意:as操作符不能用於值類型,值類型只能用Cast,而此時會產生裝箱/拆箱操作,而此時一般建議會使用is操作符來先判斷。如:
object o = Factory.GetValue();
int i = 0;
if(o is int)
i=(int)o;
is操作符應該用在不能使用as操作符進行轉換的時候。
好的面向對象設計應當避免進行類型轉換,但是有時你不得不進行類型轉換的時候,使用as和is操作符可以更清晰地表達你的目的。
4.使用條件特性來代替#if
#if/#endif太容易濫用,創建的代碼也很難理解和調試。C#增加了條件特性來指示一個方法是否應該被調用。它比#if/#endif更清晰。
#if/#endif方式:
private void CheckStateBad()
{
// The Old way:
#if DEBUG
Trace.WriteLine("Entering CheckState for Person");
// Grab the name of the calling routine:
string methodName = new StackTrace().GetFrame(1).GetMethod().Name;
Debug.Assert(lastName != null, methodName, "Last Name cannot be null");
Debug.Assert(lastName.Length > 0, methodName, "Last Name cannot be blank");
Debug.Assert(firstName != null, methodName, "First Name cannot be null");
Debug.Assert(firstName.Length > 0, methodName, "First Name cannot be blank");
Trace.WriteLine("Exiting CheckState for Person");
#endif
}
如果在Release模式編譯生成,此方法將會是一個空方法。如果頻繁地調用該方法也會產生一部分開銷。而有時因為#if/#endif的位置不當,Release編譯時會產生一些錯誤,如:
public void Func()
{
string msg = null;
#if DEBUG
msg = GetDiagnostics();
#endif
Console.WriteLine(msg);
}
如果在Release模式編譯,則此時會產生錯誤。
條件特性方式:
[Conditional("DEBUG")]
private void CheckStateBad()
{
...
}
該特性告訴編譯器該方法只有在檢測到有DEBUG環境變量時有效.
條件特性相比#if/#endif會生成更有效的IL代碼.
5、總是提供ToString()
System.Object.ToString()是.NET環境中最常用的方法之一。你應當為所有客戶端的類提供一個合理的ToString()版本。默認的System.Object.ToString()會返回類型的完全限定名,如:System.Drawing.Rect。通過實現IFormattable.ToString方法,你可以按照你自己的格式進行輸出。一般情況下你應當在所有你的類型中重寫ToString()方法,並且輸出簡短合理的信息。在特定情況下,需要輸出特定信息的時候,應當通過實現IFormattable接口和ICustomFormatter接口的相應方法來完成。