[C#.NET] 為了避免濫用 #if,使用 Conditional 屬性可以讓你降低錯誤發生
#if是蠻常用的語法,主要是在同一個專案產生出不同的編譯結果,主要是常用在debug與release版本,在Debug裡可能會有許多的測試,我不想要再Release版本裡顯示,以免拖垮效能,我便會這樣寫:
static void check()
{
#if DEBUG
Console.WriteLine("Debug Mode");
Console.ReadKey();
#endif
}
在上述程式碼裡 #if區段裡的程式碼只會在debug模式被編譯出來
以下是Debug模式產生的
以下是Release模式產生的
由上面的程式碼可以看到,不管在什麼模式下check方法都會被產生出來,縱使他在release方法下什麼事都不做,方法的呼叫和JIT編譯多少會有些開銷成本。
再來看看以下程式碼,因為粗心,把GetValue方法回傳的重要結果不小心寫錯區段,而造成在release版本一直無法得到正確的結果,不要笑,真的會有這樣的誤植!說不定你也犯過這樣的阿呆錯誤。
static void check()
{
string msg = null;
#if DEBUG
msg =GetValue();
Console.WriteLine(msg);
#else
Console.WriteLine(msg);
#endif
}
使用Conditional屬性將整個方法包起來
static void Main(string[] args)
{
check();
}
[Conditional("DEBUG")]
static void check()
{
string msg = null;
msg = GetValue();
Console.WriteLine(msg);
}
private static string GetValue()
{
return "important data";
}
這樣一來在release模式下就不會產生Conditional所定義的check方法呼叫,利用Reflector可以觀察出release模式編譯的結果
static void Main(string[] args)
{
//release模式下少了呼叫成本
}
[Conditional("DEBUG")]
static void check()
{
string msg = null;
msg = GetValue();
Console.WriteLine(msg);
}
private static string GetValue()
{
return "important data";
}
這樣看起來對於release編譯的結果的確會有改善,減少了一些不必要的成本花費。
舉個我最常用的方法,比如我要想要量測副程式所花費的時間,最常用的就是Stopwatch類別
static Stopwatch _sw = new Stopwatch();
static void Main(string[] args)
{
#if DEBUG
_sw.Reset();
_sw = Stopwatch.StartNew();
#endif
long num = 0;
for (int i = 1; i < 100000000; i++)
{
num += 1;
}
#if DEBUG
_sw.Stop();
TimeSpan el = _sw.Elapsed;
Console.WriteLine("花費 {0} ", el);
long ms = _sw.ElapsedMilliseconds;
Console.WriteLine("花費 {0} 毫秒", ms);
long tk = _sw.ElapsedTicks;
Console.WriteLine("花費 {0} ticks", tk);
#endif
Console.ReadLine();
}
這樣的程式碼看起來很髒又很亂,只要善用Conditional屬性,將DEBUG模式的東西抽離出來,便能減少主要程式碼的壞味道。
static Stopwatch _sw = new Stopwatch();
static void Main(string[] args)
{
StartLog();
long num = 0;
for (int i = 1; i < 100000000; i++)
{
num += 1;
}
StopLog();
Console.ReadLine();
}
[Conditional("DEBUG")]
static void StartLog()
{
_sw.Reset();
_sw = Stopwatch.StartNew();
}
[Conditional("DEBUG")]
static void StopLog()
{
_sw.Stop();
TimeSpan el = _sw.Elapsed;
Console.WriteLine("花費 {0} ", el);
long ms = _sw.ElapsedMilliseconds;
Console.WriteLine("花費 {0} 毫秒", ms);
long tk = _sw.ElapsedTicks;
Console.WriteLine("花費 {0} ticks", tk);
}
後記:
善用Conditional可以為我們帶來小小的優化以及減少程式碼的壞味道,又得到一把利器,離重構之路又邁向一步。
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET