I want to get all the fields of a class without getting the underlying implementations of the class event. type.GetFields(BindingFlags...) returns the nuderlying delegate for event fields. Does anyone knows how to filter them out ?
-
What do you mean? If you have an object named Container and it has a Container field, you don't want to see that field? – Kiril May 26 '10 at 00:40
-
When reflecting a type with event called NotifySomething with an Action delegate, a private field of type Action is created in the class and i do not want to get it. – Izik Shmulewitz May 27 '10 at 10:48
1 Answers
Events in .NET generate a field with the same and type as the event. In addition they generates two methods (adder and remover, which have the same name as the field with prefixes 'add_' and 'remove_').
In order to filter event backing fields you can remove fields with same name as events. You can be sure no field will be defined with same name as event since the compiler will fail the compilation if another member is defined with the same name.
For example:
public IEnumerable<FieldInfo> FilterBackingEventFields(Type type)
{
List<string> eventNames = type
.GetEvents().Select(eventInfo => eventInfo.Name).ToList();
FieldInfo[] fieldInfos = type
.GetFields(BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.Instance);
return fieldInfos.Where(fieldInfo => !eventNames.Contains(fieldInfo.Name));
}
Usage example:
public class ClassWithEventAndField
{
public event EventHandler MyEvent;
public int MyField;
}
[Test]
public void TestFieldsFilter()
{
IEnumerable<FieldInfo> fields =
FilterBackingEventFields(typeof(ClassWithEventAndField));
FieldInfo expectedField = typeof(ClassWithEventAndField).GetField("MyField");
Assert.That(fields, Is.EquivalentTo(new[] { expectedField }));
}
EDIT: added support to work with VB and C#
This code will work on auto generated events (custom adder or remover will break the code). This is also a risky code, it makes some assumptions on the way adder method is generated and compiles. I am posting this code as "Academic" information, I wouldn't use it in production code.
public IEnumerable<FieldInfo> FilterBackingEventFields(Type type)
{
List<int> backingFieldsTokens = type
.GetEvents().Select(eventInfo => MetadataToken(eventInfo)).ToList();
FieldInfo[] fieldInfos = type
.GetFields(BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.Instance);
return fieldInfos
.Where(fieldInfo => !backingFieldsTokens.Contains(fieldInfo.MetadataToken));
}
private static int MetadataToken(EventInfo eventInfo)
{
MethodInfo adderMethod = eventInfo.GetAddMethod();
int fieldToken =
adderMethod.GetMethodBody().GetILAsByteArray()[3] |
adderMethod.GetMethodBody().GetILAsByteArray()[4] << 8 |
adderMethod.GetMethodBody().GetILAsByteArray()[5] << 16 |
adderMethod.GetMethodBody().GetILAsByteArray()[6] << 24;
return fieldToken;
}
The assumption made here is that the bytes 3-6 in the adder method body are the token of the backing field of the event. I really hope someone will post an elegant and safe solution to this problem :)

- 23,310
- 6
- 60
- 75
-
I hoped for some better support from reflection API. I know I can do it like this but then I will have to implement it differently for VB (the underlying field for event called X is XEvent). – Izik Shmulewitz Jun 05 '10 at 11:33
-
@Izik Shmulewitz, you're right, I didn't even know that VB generates different backing field. I added posted some code that solves this, but it'll work only on events with default adder & remove methods. – Elisha Jun 05 '10 at 13:07