如何过滤是类事件底层实现的FieldInfos?

3

我想获取一个类的所有字段,但不包括获取该类事件的底层实现。 type.GetFields(BindingFlags...)会返回事件字段的底层委托。有谁知道如何过滤掉它们?


什么意思?如果你有一个名为Container的对象,并且它有一个Container字段,你不想看到那个字段吗? - Kiril
当使用 Action 委托反射具有名为 NotifySomething 的事件的类型时,类中会创建一个私有字段,其类型为 Action,而我不想获取它。 - Izik Shmulewitz
1个回答

2

.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;
}

这里的假设是 adder 方法主体中字节 3-6 是事件后备字段的令牌。我真的希望有人能发布一个优雅且安全的解决方案来解决这个问题 :)

我希望反射API能够提供更好的支持。我知道我可以这样做,但是我将不得不为VB实现不同的方法(事件X的基础字段称为XEvent)。 - Izik Shmulewitz
@Izik Shmulewitz,你说得对,我甚至不知道VB会生成不同的后备字段。我添加了一些代码来解决这个问题,但它只适用于具有默认添加和删除方法的事件。 - Elisha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接