【问题标题】:How can I filter FieldInfos that are the underlying implementation of a class event?如何过滤作为类事件底层实现的 FieldInfos?
【发布时间】:2010-05-25 15:14:00
【问题描述】:

我想在不获取类事件的底层实现的情况下获取类的所有字段。 type.GetFields(BindingFlags...) 返回事件字段的裸体委托。有谁知道如何过滤掉它们?

【问题讨论】:

  • 什么意思?如果您有一个名为 Container 的对象并且它有一个 Container 字段,您不想看到该字段吗?
  • 当用一个 Action 委托反映一个名为 NotifySomething 的事件类型时,在类中创建了一个 Action 类型的私有字段,我不想得到它。

标签: c# .net reflection fieldinfo


【解决方案1】:

.NET 中的事件生成一个与事件具有相同类型和类型的字段。此外,它们还生成两个方法(adder 和 remover,它们与前缀为“add_”和“remove_”的字段同名)。

为了过滤事件支持字段,您可以删除与事件同名的字段。您可以确定不会定义与 event 同名的字段,因为如果定义了另一个成员同名,编译器将无法编译。

例如:

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# 的支持

此代码适用于自动生成的事件(自定义加法器或移除器会破坏代码)。这也是一个有风险的代码,它对 adder 方法的生成和编译方式做了一些假设。我将此代码发布为“学术”信息,我不会在生产代码中使用它。

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 是事件的支持字段的标记。我真的希望有人会为这个问题发布一个优雅而安全的解决方案:)

【讨论】:

  • 我希望反射 API 能提供更好的支持。我知道我可以这样做,但是我将不得不为 VB 以不同的方式实现它(称为 X 的事件的基础字段是 XEvent)。
  • @Izik Shmulewitz,你说得对,我什至不知道 VB 会生成不同的支持字段。我添加了一些解决此问题的代码,但它仅适用于具有默认加法器和删除方法的事件。
猜你喜欢
  • 2010-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-10
相关资源
最近更新 更多