[C#.NET] 為了避免濫用 #if,使用 Conditional 屬性可以讓你降低錯誤發生

  • 39355
  • 0
  • 2013-07-05

[C#.NET] 為了避免濫用 #if,使用 Conditional 屬性可以讓你降低錯誤發生

#if是蠻常用的語法,主要是在同一個專案產生出不同的編譯結果,主要是常用在debug與release版本,在Debug裡可能會有許多的測試,我不想要再Release版本裡顯示,以免拖垮效能,我便會這樣寫:


        static void check()
        {
#if DEBUG
            Console.WriteLine("Debug Mode");
            Console.ReadKey();
#endif
        }

 

 

在上述程式碼裡 #if區段裡的程式碼只會在debug模式被編譯出來

image

 

以下是Debug模式產生的

image

 

以下是Release模式產生的

image

 

由上面的程式碼可以看到,不管在什麼模式下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編譯的結果的確會有改善,減少了一些不必要的成本花費。

image


舉個我最常用的方法,比如我要想要量測副程式所花費的時間,最常用的就是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

Image result for microsoft+mvp+logo