Как я могу отфильтровать FieldInfos, которые являются базовой реализацией события класса? - PullRequest
2 голосов
/ 25 мая 2010

Я хочу получить все поля класса без получения базовых реализаций события класса. type.GetFields (BindingFlags ...) возвращает делегат nuderlying для полей событий. Кто-нибудь знает, как их отфильтровать?

1 Ответ

1 голос
/ 05 июня 2010

События в .NET генерируют поле с тем же типом и типом события.Кроме того, они генерируют два метода (adder и remover, которые имеют то же имя, что и поле с префиксами 'add_' и 'remove _').

Чтобы отфильтровать поля поддержки событий, вы можете удалить поля с одинаковыми именами.как события.Вы можете быть уверены, что ни одно поле не будет определено с тем же именем, что и событие, так как компилятор не сможет выполнить компиляцию, если другой член определен с тем же именем.

Например:

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 в теле метода сумматора являются токеномвспомогательное поле события.Я действительно надеюсь, что кто-то опубликует элегантное и безопасное решение этой проблемы:)

...