如何通过反射获取类型的事件,忽略父接口上的事件

本文关键字:事件 接口 获取 取类型 何通过 反射 | 更新日期: 2023-09-27 18:08:05

我有以下代码

Type type = ...
var events=type.GetEvents(
    BindingFlags.DeclaredOnly 
    | BindingFlags.Instance 
    | BindingFlags.Public).ToList(); 

然而,这也返回了声明的事件父接口。例如

UIElement
ContentElement
实施

IInputElement

定义事件

//
// Summary:
//     Occurs when the mouse pointer moves while the mouse pointer is over the element.
event MouseEventHandler PreviewMouseMove;

,但上面的调用geteevents与所有的绑定标志设置如上返回接口和两个具体类的事件。

如何过滤掉定义在父接口上的事件GetEvents吗?

请注意,我正在为每个事件生成扩展方法,如下所示

public static 
IObservable<EventPattern<MouseButtonEventArgs>> 
PreviewMouseLeftButtonDownObserver(this IInputElement This){
        return Observable
               .FromEventPattern
               <MouseButtonEventHandler, MouseButtonEventArgs>
               ( h => This.PreviewMouseLeftButtonDown += h
               , h => This.PreviewMouseLeftButtonDown -= h);
}

所以我只做了事件的根定义,而不是派生的虚拟或接口实现。

如何通过反射获取类型的事件,忽略父接口上的事件

示例中:

interface IFace
{
    event EventHandler A;
}
class Base
{
    public virtual event EventHandler B;
    public event EventHandler C;
}
class YourType : Base, IFace
{
    public event EventHandler A;          // implements interface
    public override event EventHandler B; // overrides base class implementation
    public event EventHandler D;          // new event
}

如果您在typeof(YourType)上使用反射,您想查看哪个事件?

答:

你可以这样写:

events.Where(x =>
  x.GetAddMethod().GetBaseDefinition().DeclaringType == type
  && !x.GetAddMethod().IsFinal)
  );

,其中type在你的问题中是这样的。第一个条件确保它不是在父类型上定义的事件。第二个标准确保它不是接口实现。您可能不需要第二个条件,这取决于您的确切设置。

编辑回答

events.Where(x =>{ 
   var addMethod = x.GetAddMethod();
   var basetype = addMethod.GetBaseDefinition().DeclaringType;
   return basetype == type && (!addMethod.IsFinal || basetype.IsInterface);
}) 

这个添加确保我也得到接口。

替代方法:

要确定成员是否实现了某个接口,可以使用:

var interfaceImplementingMethods = new HashSet<MethodInfo>(type.GetInterfaces()
  .SelectMany(i => type.GetInterfaceMap(i).TargetMethods));
var result = events.Where(x => !interfaceImplementingMethods
  .Contains(x.GetAddMethod()));

即使实现成员被声明为virtual(或abstract),它也应该工作。当然,如果您想要排除同样继承的成员,则必须与GetBaseDefinition()结合使用。

您可以尝试使用linq过滤掉接口的事件,像这样…

Type type = typeof (Bar);
Type interfaceType = typeof (IFoo);
var interfaceEvents = interfaceType.GetEvents(BindingFlags.DeclaredOnly 
                                              | BindingFlags.Instance 
                                              | BindingFlags.Public);
var events = type.GetEvents(BindingFlags.DeclaredOnly 
                            | BindingFlags.Instance 
                            | BindingFlags.Public);
events = events.Where(e => interfaceEvents.FirstOrDefault(
                ie => ie.Name == e.Name && 
                ie.EventHandlerType == e.EventHandlerType) == null).ToArray();
编辑:


基类中的事件不应该包含在GetEvents方法的结果中。这里是一个方法,它删除该类型实现的所有接口的事件。

编辑2:
此方法还将删除重写的基类事件:

public IEnumerable<EventInfo> GetEventsEx(Type type)
{
    var baseEvents = new List<EventInfo>();
    // Adds Events of interfaces to baseEvents
    foreach (var interfaceType in type.GetInterfaces())
    {
        baseEvents.AddRange(interfaceType.GetEvents(
            BindingFlags.DeclaredOnly
            | BindingFlags.Instance
            | BindingFlags.Public));
    }
    // Adds Events of base classes to baseEvents
    var baseType = type.BaseType;
    while (baseType != typeof (object))
    {
        baseEvents.AddRange(baseType.GetEvents(
            BindingFlags.DeclaredOnly
            | BindingFlags.Instance
            | BindingFlags.Public));
        baseType = baseType.BaseType;
    }
    // Get events for type
    var events = type.GetEvents(
        BindingFlags.DeclaredOnly
        | BindingFlags.Instance
        | BindingFlags.Public);
    // Remove baseEvents and return
    return events.Where(e => baseEvents.FirstOrDefault(
            ie => ie.Name == e.Name &&
            ie.EventHandlerType == e.EventHandlerType) == null);
}

编辑3:删除BindingFlags.DeclaredOnly标志的新方法,这可能会工作得更好:

public IEnumerable<EventInfo> GetEventsEx(Type type)
{
    var baseEvents = new List<EventInfo>();
    // Adds Events of interfaces to baseEvents
    foreach (var interfaceType in type.GetInterfaces())
    {
        baseEvents.AddRange(interfaceType.GetEvents(
            BindingFlags.Instance
            | BindingFlags.Public));
    }
    // Adds Events of base classes to baseEvents
    var baseType = type.BaseType;
    if (baseType != null && baseType != typeof (object))
    {
        baseEvents.AddRange(baseType.GetEvents(
            BindingFlags.Instance
            | BindingFlags.Public));
    }
    // Get events for type
    var events = type.GetEvents(
        BindingFlags.DeclaredOnly
        | BindingFlags.Instance
        | BindingFlags.Public);
    // Remove baseEvents and return
    return events.Where(e => baseEvents.FirstOrDefault(
            ie => ie.Name == e.Name &&
            ie.EventHandlerType == e.EventHandlerType) == null);
}