反射:在运行时区分事件字段和委托类型字段

时间:2010-08-29 02:39:42

标签: c# events reflection

我的主要问题是: 是否有可能在反射中将某个委托类型的字段与事件用作存储字段的字段区分开来? 这归结为一个问题:FieldInfo类是否包含有关它是否属于某个事件的信息,如存储字段?我找不到任何可能告诉的属性,也没有找到属性属性。

在下面的代码中,SomeField和SomeEvent的FieldInfos的相关属性是相同的。因此,我不知道如何根据FieldInfos是否为eventstoragefield来对其进行排序。

using System;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace Test
{
    class Program
    {
        public Action SomeField;
        public event Action SomeEvent;
        static void Main(string[] args)
        {
            FieldInfo[] fields = typeof(Program).GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
            foreach (FieldInfo fi in fields)
               Console.WriteLine(string.Format("FieldName: {0}, Accessibility: {1}, Has Attributes: {2}.", fi.Name, fi.Attributes,
                    fi.GetCustomAttributes(true).Length != 0));
            Console.ReadLine();
        }
    }
}

一种解决方案是搜索名称完全相同的eventInfo,但我不知道这是否万无一失,坦率地说,我不满意这个解决方案。必须有更直接的方式。

4 个答案:

答案 0 :(得分:2)

您已定义了类似字段的事件:

C#语言规范:

  

编译类似字段的事件时,编译器会自动创建存储来保存委托,并为事件添加或删除事件处理程序创建访问器。

未指定编译器如何为存储字段生成名称。通常,此字段名称与事件名称匹配,因此您将拥有两个具有相同名称的成员(示例中为SomeName),但不同的成员类型和不同的可见性(事件 - 公共,字段 - 私有)。

Type.GetMember()

  

GetMembers方法不会按特定顺序返回成员,例如按字母顺序或声明顺序。您的代码不得依赖于返回成员的顺序,因为该顺序会有所不同。

如果你在没有参数的情况下超载GetMembers()那么它应该只返回公共成员 - 事件。但是,如果您使用另一个重载(接受BindingFlags)与BindingFlags.NonPublic - 那么它将以未指定的顺序返回字段和事件,因此您不能依赖于您获得的第一个元素将是事件。

答案 1 :(得分:2)

使用MemberInfo.MemberType属性。它将返回Field / Event。 RtFieldInfo是表示字段(fieldInfo.GetType())的FieldInfo对象的类型,而不是字段的类型(fieldInfo.MemberType)。

class Program {
    public event Action<Program> SomeEvent;
    public Action<Program> SomeField;

    static void Main(string[] args) {
        var members = typeof (Program).GetMembers();

        var eventField = members.First(mi => mi.Name == "SomeEvent");
        var normalField = members.First(mi => mi.Name == "SomeField");

        Console.WriteLine("eventField.MemberType: {0}", eventField.MemberType);
        Console.WriteLine("normalField.MemberType: {0}", normalField.MemberType);
    }
}

答案 2 :(得分:0)

您的示例中的代码无法编译,但是,以下两项都执行:

class Program
{
    public event Action<Program> SomeEvent;

    static void Main(string[] args)
    {
        var test = typeof(Program).GetMembers().First((mi) => mi.Name == "SomeEvent");
        Console.WriteLine(test.GetType());
    }
}

class Program
{
    public event Action<Program> SomeEvent;

    static void Main(string[] args)
    {
        var test = typeof(Program).GetMembers().First((mi) => {return mi.Name == "SomeEvent";});
        Console.WriteLine(test.GetType());
    }
}

两者都产生基类MemberInfo的结果,因为这是GetMember()返回的,实际类型为RuntimeEventInfo,并输出到控制台。

此示例代码是否与某些重要方面的实际代码不同?

答案 3 :(得分:-1)

简短回答:不,没有万无一失的方法可以做到这一点。甚至不必是事件的支持委托字段。如果存在,则它们之间的元数据没有关联。