How to get events of type by reflection ignoring events on parent interfaces

回眸只為那壹抹淺笑 提交于 2019-12-10 20:08:19

问题


I have the following code

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

However this is also returning me events declared on parent interfaces. For example

Both

UIElement
ContentElement

implement

IInputElement

which defines the event

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

but the above call to GetEvents with all the binding flags set as above returns the event for the interface as well as the two concrete classes.

How can I filter out events that are defined on parent interfaces from GetEvents?

Note that I am generating extensions methods for each event like so

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

so I only did the root definition of the event not derived virtual or interface implementations.


回答1:


In the example:

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
}

which event do you want to see if you use reflection on typeof(YourType)?

ANSWER:

You can use something like:

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

where type is like in your question. The first criterion makes sure it is not an event defined on a parent type. The second criterion makes sure it's not an interface implementation. You might not need the second criterion, depending on your exact setup.

EDIT to ANSWER

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

This addition ensures that I get the interfaces as well.

ALTERNATIVE APPROACH:

To definitely determine if a member implements some interface or not, one can use this:

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

It should work even if the implementing member is declared virtual (or abstract). This must be combined with GetBaseDefinition() if you want to exclude also inherited members, of course.




回答2:


You could try to filter out the events of the interface using linq, like this...

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();

Edit:
Events from base classes should not be included in the result from GetEvents method. Here is a method that removes the events of all interfaces the type implements.

Edit 2:
This method will also remove overridden base class events:

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

Edit 3: New method with the BindingFlags.DeclaredOnly flags removed, this will probably work a little better:

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


来源:https://stackoverflow.com/questions/15895091/how-to-get-events-of-type-by-reflection-ignoring-events-on-parent-interfaces

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!