はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】Conditional属性は呼び出し元のシンボル定義を参照するので注意、またその対処法

はじめに

今回はConditionalAttributeは呼び出し元のシンボル定義を参照していることについて取り上げたいと思います。

learn.microsoft.com

メソッド呼び出しがスキップされる場合とされない場合

概要

そもそもConditional属性とは?という方もいると思うので公式の説明を載せておきます。

指定した条件付きコンパイル シンボルが定義されていない場合、メソッド呼び出しまたは属性を無視するようコンパイラに指示します。

learn.microsoft.com

// 公式サンプル
#define CONDITION1
#define CONDITION2
using System;
using System.Diagnostics;

class Test
{
    static void Main()
    {
        Console.WriteLine("Calling Method1");
        Method1(3);
        Console.WriteLine("Calling Method2");
        Method2();

        Console.WriteLine("Using the Debug class");
        Debug.Listeners.Add(new ConsoleTraceListener());
        Debug.WriteLine("DEBUG is defined");
    }

    [Conditional("CONDITION1")]
    public static void Method1(int x)
    {
        Console.WriteLine("CONDITION1 is defined");
    }

    [Conditional("CONDITION1"), Conditional("CONDITION2")]
    public static void Method2()
    {
        Console.WriteLine("CONDITION1 or CONDITION2 is defined");
    }
}

ConditionalAttribute クラス (System.Diagnostics) | Microsoft Learn

また昔私もConditional属性について書いたので、よければそちらもどうぞ。
www.hanachiru-blog.com

気をつけたいこと

最初「Conditionalが定義されている側」にシンボル定義をすれば良いのかなと思っていたのですが、どうやら「呼び出し元側」にシンボル定義がされていないといけないみたいです。

// メソッドの呼び出し側にシンボル定義を行ったときにはメソッド呼び出しがスキップされない
#define ENABLE_HOGE

public static class Program
{
    public static void Main(string[] args)
    {
        Program2.Hoge();
    }
}
public static class Program2
{
    [Conditional("ENABLE_HOGE")]
    public static void Hoge()
    {
        Console.WriteLine("Hoge");
    }
}
メソッド呼び出しがスキップされる場合とされない場合

対処法

Conditionalが定義されている側」のシンボル定義によって、メソッド呼び出しをスキップするかしないかを変更したい場合は以下のように書くことで実現できます。

#define ENABLE_HOGE

public static class Program2
{
    // ENABLE_HOGEがシンボル定義されていたら、Hogeメソッド呼び出しがスキップされる
#if !ENABLE_HOGE
    [Conditional("絶対に定義されないような文字列")]
#endif
    public static void Hoge()
    {
        // Conditional属性を用いていてもHogeメソッドはコンパイルに含まれるので、コードサイズを減らしたいなら#ifで囲っておくのが吉
#if ENABLE_HOGE
        Console.WriteLine("Hoge");
#endif
    }
}

またコメントにも書いておきましたが、Conditionalはあくまで呼び出し元のメソッド呼び出しがスキップされるかどうかのみ影響されます。ですので、メソッド自体はコンパイルに含まれます。

コードサイズを少しでも減らしたい場合は#if ~ #endifで囲っておくのが吉でしょう。