问题
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