locked
FieldInfo for public Event handler wrong? RRS feed

  • Question

  • In a class that declares a public event handler:

    public event EventHandler MyEvent;

    On examining the assembly with reflection to get the fields for the class, the FieldInfo for the given event indicates that the field is private i.e. FieldInfo.IsPrivate is true and FieldInfo.IsPublic is false, how does one obtain the correct qualifier (public, protected, private or internal)?

    Thank you.




    • Edited by ellipsisware Thursday, February 4, 2016 11:25 PM another typo!
    Thursday, February 4, 2016 11:22 PM

Answers

  • It seems that I was missing the point that the FieldInfo does not contain information about the 'event' in:

    public event EventHandler MyEvent;

    So what is wanted is the MemberInfo for one of the event methods that use the private field in the class where the event is declared, the following seems to do this without consideration of nested classes with events or static events.

    For a FieldInfo field representing a field in a class:

    Type genericType = fieldType.IsGenericType ?
        fieldType.GetGenericTypeDefinition() : null;
    bool isGeneric = null != genericType;
    if (typeof(EventHandler).IsAssignableFrom(fieldType) ||
        (isGeneric &&
        typeof(EventHandler<>).IsAssignableFrom(genericType)) ||
        (isGeneric && (null != genericType.BaseType) &&
        typeof(MulticastDelegate).IsAssignableFrom(genericType.BaseType)))

    { EventInfo[] events = type.GetEvents(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (EventInfo eventInfo in events) { if (eventInfo.Name == field.Name && eventInfo.EventHandlerType == fieldType) { MethodInfo mi = eventInfo.AddMethod; if (mi.IsPublic) { qualifier = "public"; break; } else if (mi.IsPrivate) { qualifier = "private"; break; } else if (mi.IsFamily) { qualifier = "protected"; break; } else if (mi.IsAssembly) { qualifier = "internal"; break; } else if ((mi.Attributes & MethodAttributes.FamORAssem) == MethodAttributes.FamORAssem) { qualifier = "protected internal"; break; } } }

    }




    • Marked as answer by ellipsisware Monday, February 8, 2016 7:33 PM
    • Edited by ellipsisware Thursday, February 11, 2016 7:36 PM wrong order of IsAssignable
    Sunday, February 7, 2016 8:00 PM

All replies

  • What exactly do you mean by "correct qualifier"?

    public & co. are C# specific, the reflection API doesn't directly provide this information, it's up to you to map properties like IsPublic, IsPrivate, IsFamily to C# access modifiers.

    Or are you referring to the access modifier of the event itself? That cannot be obtained from the field info unless you are willing to assume that the event and the field have the same name. But that's not always true as the add/remove event accessors can be customized and can use any field or none at all.

    Friday, February 5, 2016 6:22 AM
  • I am referring to the modifier of the access event - the 'public" in "public event EventHandler MyEvent".

    For the field named MyEvent I would expect FieldInfo.IsPublic to return true - not false and I would expect FieldInfo.IsPrivate to return false - not true - is this unreasonable?


    Friday, February 5, 2016 7:25 PM
  • "For the field named MyEvent I would expect FieldInfo.IsPublic to return true"

    Why? The event is public but the field is private.

    Friday, February 5, 2016 7:47 PM
  • It seems that I was missing the point that the FieldInfo does not contain information about the 'event' in:

    public event EventHandler MyEvent;

    So what is wanted is the MemberInfo for one of the event methods that use the private field in the class where the event is declared, the following seems to do this without consideration of nested classes with events or static events.

    For a FieldInfo field representing a field in a class:

    Type genericType = fieldType.IsGenericType ?
        fieldType.GetGenericTypeDefinition() : null;
    bool isGeneric = null != genericType;
    if (typeof(EventHandler).IsAssignableFrom(fieldType) ||
        (isGeneric &&
        typeof(EventHandler<>).IsAssignableFrom(genericType)) ||
        (isGeneric && (null != genericType.BaseType) &&
        typeof(MulticastDelegate).IsAssignableFrom(genericType.BaseType)))

    { EventInfo[] events = type.GetEvents(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (EventInfo eventInfo in events) { if (eventInfo.Name == field.Name && eventInfo.EventHandlerType == fieldType) { MethodInfo mi = eventInfo.AddMethod; if (mi.IsPublic) { qualifier = "public"; break; } else if (mi.IsPrivate) { qualifier = "private"; break; } else if (mi.IsFamily) { qualifier = "protected"; break; } else if (mi.IsAssembly) { qualifier = "internal"; break; } else if ((mi.Attributes & MethodAttributes.FamORAssem) == MethodAttributes.FamORAssem) { qualifier = "protected internal"; break; } } }

    }




    • Marked as answer by ellipsisware Monday, February 8, 2016 7:33 PM
    • Edited by ellipsisware Thursday, February 11, 2016 7:36 PM wrong order of IsAssignable
    Sunday, February 7, 2016 8:00 PM
  • It does what you want but only for events that have auto-generated add/remove accessors. It won't work for this kind of code:

    class Test
    {
        private EventHandler _foo;
    
        public event EventHandler Foo
        {
            add { _foo += value; }
            remove { _foo -= value; }
        }
    }
    

    • Marked as answer by ellipsisware Monday, February 8, 2016 7:33 PM
    • Unmarked as answer by ellipsisware Monday, February 8, 2016 7:33 PM
    Sunday, February 7, 2016 8:07 PM
  • I realize that but for me it is not an issue as I only use auto generated event accessors - if there is a simple solution for this I would be interested though.

    Sunday, February 7, 2016 8:39 PM
  • "if there is a simple solution for this I would be interested though."

    The only solution would be to analyze the IL of the accessor methods but that's complicated. Also note that it is possible for such accessors to not use delegate type fields at all. WPF and WinForms events commonly store the delegates in dictionaries in order to minimize the object size.

    Monday, February 8, 2016 6:02 AM
  • Fortunately I am not concerned with WinForms or WPF at present so I think that I can go with the posted code.

    Thank you.

    Monday, February 8, 2016 7:32 PM