3

クラス イベントの基になる実装を取得せずに、クラスのすべてのフィールドを取得したいと考えています。type.GetFields(BindingFlags...) は、イベント フィールドのヌードデリゲートを返します。それらを除外する方法を知っている人はいますか?

4

1 に答える 1

2

.NETのイベントは、イベントと同じタイプのフィールドを生成します。さらに、2つのメソッド(接頭辞「add_」および「remove_」が付いたフィールドと同じ名前のadderおよびremover)を生成します。

イベントバッキングフィールドをフィルタリングするために、イベントと同じ名前のフィールドを削除できます。別のメンバーが同じ名前で定義されている場合、コンパイラーはコンパイルに失敗するため、イベントと同じ名前でフィールドが定義されないことを確認できます。

例えば:

public IEnumerable<FieldInfo> FilterBackingEventFields(Type type)
{
    List<string> eventNames = type
        .GetEvents().Select(eventInfo => eventInfo.Name).ToList();

    FieldInfo[] fieldInfos = type
        .GetFields(BindingFlags.NonPublic | 
                   BindingFlags.Public | 
                   BindingFlags.Instance);

    return fieldInfos.Where(fieldInfo => !eventNames.Contains(fieldInfo.Name));
}

使用例:

public class ClassWithEventAndField
{
    public event EventHandler MyEvent;
    public int MyField;
}

[Test]
public void TestFieldsFilter()
{
    IEnumerable<FieldInfo> fields = 
        FilterBackingEventFields(typeof(ClassWithEventAndField));

    FieldInfo expectedField = typeof(ClassWithEventAndField).GetField("MyField");
    Assert.That(fields, Is.EquivalentTo(new[] { expectedField }));
}

編集: VBおよびC#で動作するためのサポートが追加されました

このコードは、自動生成されたイベントで機能します(カスタムの加算器またはリムーバーはコードを壊します)。これも危険なコードであり、加算器メソッドが生成およびコンパイルされる方法についていくつかの仮定を行います。このコードを「アカデミック」情報として投稿しています。本番コードでは使用しません。

public IEnumerable<FieldInfo> FilterBackingEventFields(Type type)
{
    List<int> backingFieldsTokens = type
        .GetEvents().Select(eventInfo => MetadataToken(eventInfo)).ToList();

    FieldInfo[] fieldInfos = type
        .GetFields(BindingFlags.NonPublic | 
                   BindingFlags.Public | 
                   BindingFlags.Instance);

    return fieldInfos
     .Where(fieldInfo => !backingFieldsTokens.Contains(fieldInfo.MetadataToken));
}

private static int MetadataToken(EventInfo eventInfo)
{
    MethodInfo adderMethod = eventInfo.GetAddMethod();
    int fieldToken =
        adderMethod.GetMethodBody().GetILAsByteArray()[3] |
        adderMethod.GetMethodBody().GetILAsByteArray()[4] << 8 |
        adderMethod.GetMethodBody().GetILAsByteArray()[5] << 16 |
        adderMethod.GetMethodBody().GetILAsByteArray()[6] << 24;

    return fieldToken;
}

ここでの前提は、加算器メソッド本体の3〜6バイトがイベントのバッキングフィールドのトークンであるということです。私は本当に誰かがこの問題に対するエレガントで安全な解決策を投稿することを願っています:)

于 2010-06-05T05:40:38.797 に答える