Unity - Basic - Preprocessor Directives

前述

中文稱作 : 前置處理器指示詞

筆者最近接觸到的遺舊專案中發現裡面大量地使用 條件式編譯

#if DEBUG
    Console.WriteLine("Debug version");
#endif

筆者之前有使用也基本只使用 定義區域 (排版效果)

#region MyClass definition
    public class MyClass
    {
        static void Main(){...}
    }
#endregion

Unity 在處理平台裝置時也蠻常會出現的

public class PlatformDefines : MonoBehaviour 
{
  void Start () 
  {
    #if UNITY_EDITOR
      Debug.Log("Unity Editor");
    #endif

    #if UNITY_IOS
      Debug.Log("iOS");
    #endif

    #if UNITY_STANDALONE_OSX
        Debug.Log("Standalone OSX");
    #endif

    #if UNITY_STANDALONE_WIN
      Debug.Log("Standalone Windows");
    #endif
  }          
}

問題

那前置處理器指示詞有什麼問題呢? (參考日文那個 Ref 有比較明確的 Case)

  • 編譯版本至少會是 2^(指示詞的分類數)種,持續的調試和測試變得非常困難。
  • Unit Test 中難以使用。
  • 編譯檢查不起作用。
  • 當巢狀結構出現時可讀性將大為降低。

上述這些狀況都會導致 延後發現問題的時間 !

方案

這個前置處理器指示詞一直是個蠻有爭議的作法,有一派人士是不使用的(通過使用條件分支、策略模式或依賴注入等其他方式,更可以清晰地表達了代碼的邏輯,提高了程式的可維護性)。

筆者也傾向不使用(業務邏輯不用,其餘的看狀況)。

Case. Editor

有時後會有需要客製編輯器時,會這樣跟 MonoBehaviour 寫在一起並用 UNITY_EDITOR 處理。但事實上獨立一個檔案放在 Editor 資料夾 應該會更好。

#if UNITY_EDITOR
...
#endif

Case. RuntimePlatform

public void Run() 
{
    if (Application.platform == RuntimePlatform.Android) 
    {
        new ClassForAndroid().Execute();
    } 
    else if ...
}

Case. 減少指示詞的影響範圍(非得使用指示詞)

ConditionalAttribute 官方文件也有推薦此作法。

[Conditional("DEBUG")]
private void DebugLog(string message)
{
    Debug.Log(message);
}

透過定義介面(需求上允許的話),來讓影響範圍控制在實例化階段。

#if UNITY_EDITOR
var runner = new EditorRunner();
#elif UNITY_IOS
var runner = new IOSRunner();
#elif UNITY_ANDROID
var runner = new AndroidRunner();
#endif

Ref