Cooooding!!

Unity(C#)を使ったゲーム開発関連Tipsなど

FieldInfoが自動実装されたフィールドのものか判定する【C#】

概要

プロパティの実装を省略する自動実装プロパティ(Auto-Implemented Properties)を使うと自動的に見えないフィールドが追加されます。このフィールドはBackingFieldと呼ばれます。BackingFieldは普通にアクセスすることはできませんが、Reflectionを使って全てのFieldInfoを取得するとその中にBackingFieldのFieldInfoが含まれます。BackingFieldのFieldInfoを除外するために区別する方法を調べてみました。

public class Hoge
{
    // 自動実装プロパティを使わない場合
    private int m_A;
    public int a { get { return m_A; } set { m_A = value; } }

    // 自動実装プロパティを使う場合("<b>k__BackingField"というフィールドが追加される)
    public int b { get; set; }
}

判別方法

自動実装されたフィールドかどうかは以下のようにAttributeをチェックすることで判定できます。

public static bool IsBackingField(FieldInfo field)
{
    return field.IsDefined(typeof(CompilerGeneratedAttribute), false);
}

ただし、自動実装プロパティ以外にもフィールドが自動実装されるパターンがあるため、自動実装プロパティによって追加されたフィールドであるかどうかの判定には使えません。

自動実装プロパティと同じように、実装を省略したeventを定義すると自動的にフィールドが追加されます。

public class Hoge
{
    // 自動でフィールドが追加されないevent
    public event System.Action action0{ add { } remove { } }
    // 自動でフィールドが追加されるevent("action1"というフィールドが追加される)
    public event System.Action action1;
}

これらはフィールドの名前で区別することができます。

/// <summary>
/// 自動実装プロパティによって追加されたフィールドかどうかを判定する
/// </summary>
public static bool IsPropertyBackingField(FieldInfo field)
{
    return IsBackingField(field) && field.Name[0] == '<';
}

/// <summary>
/// 自動が省略されたeventによって追加されたフィールドかどうかを判定する
/// </summary>
public static bool IsEventBackingField(FieldInfo field)
{
    return IsBackingField(field) && field.Name[0] != '<';
}

サンプル

private class Hoge
{
    // 自動でフィールドが追加されないevent
    public event System.Action action0{ add { } remove { } }
    // 自動でフィールドが追加されるevent
    public event System.Action action1;
    // 自動実装プロパティを使わない場合
    private int m_A;
    public int a { get { return m_A; } set { m_A = value; } }
    // 自動実装プロパティを使う場合
    public int b { get; set; }
}

public void IsBackingField()
{
    var flags = BindingFlags.Public |
            BindingFlags.NonPublic |
            BindingFlags.Instance;
    foreach(var field in typeof(Hoge).GetFields(flags))
    {
        UnityEngine.Debug.LogFormat(
            "{0}: {1}, {2}, {3}",
            field.Name,
            IsBackingField(field),
            IsPropertyBackingField(field),
            IsEventBackingField(field)
        );
    }
}

実行結果
(フィールド名: 自動実装されたものか, プロパティによるものか, イベントによるものか)

action1: True, False, True
m_A: False, False, False
<b>k__BackingField: True, True, False

環境

  • Unity 2019.1.9f1
  • VisualStudio 2019