Reflection: Distinguishing event-field from field of delegate type on runtime
The main question I have is: Is it possible in reflection to distinguish a field of some delegate type from a field which is used by an event as storagefield? That comes down to the question: does the FieldInfo class contain information about whether it belongs to an event, as storagefield ? I can't find any properties which might tell, nor custum attributes.
In the code below, the relevant properties of both FieldInfos of SomeField and开发者_JS百科 SomeEvent are identical. So I don't know how to sort FieldInfos based on whether they are eventstoragefields or not.
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Test
{
class Program
{
public Action SomeField;
public event Action SomeEvent;
static void Main(string[] args)
{
FieldInfo[] fields = typeof(Program).GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo fi in fields)
Console.WriteLine(string.Format("FieldName: {0}, Accessibility: {1}, Has Attributes: {2}.", fi.Name, fi.Attributes,
fi.GetCustomAttributes(true).Length != 0));
Console.ReadLine();
}
}
}
One solution is to search for an eventInfo with exactly the same name, but I don't know whether that is foolproof and frankly, I would not be satisfied with that solution. There must be a more direct way.
You have defined field-like event:
C# language spec:
When compiling a field-like event, the compiler automatically creates storage to hold the delegate, and creates accessors for the event that add or remove event handlers to the delegate field.
How compiler generates name for storage field is unspecified. Usually this name of field matches with the name of event, so you will have two members with the same name (SomeName in your example), but different member types and different visibility (event - public, field - private).
Type.GetMember():
The GetMembers method does not return members in a particular order, such as alphabetical or declaration order. Your code must not depend on the order in which members are returned, because that order varies.
If you take overload of GetMembers() without parameters then it should return only public members - event. However if you use another overload (that accepts BindingFlags) with BindingFlags.NonPublic - then it will return both field and event in unspecified order, so you can't rely that first element you get will be event.
Use the MemberInfo.MemberType property. It will return Field/Event. RtFieldInfo is the type of the FieldInfo object representing the field (fieldInfo.GetType()), not the type of field (fieldInfo.MemberType).
class Program {
public event Action<Program> SomeEvent;
public Action<Program> SomeField;
static void Main(string[] args) {
var members = typeof (Program).GetMembers();
var eventField = members.First(mi => mi.Name == "SomeEvent");
var normalField = members.First(mi => mi.Name == "SomeField");
Console.WriteLine("eventField.MemberType: {0}", eventField.MemberType);
Console.WriteLine("normalField.MemberType: {0}", normalField.MemberType);
}
}
Short answer: No, there's no fool proof way to do it. There doesn't even have to be a backing delegate field for an event. And if there is, there's no connection in metadata between them.
The code in your example doesn't compile, however, both of the following do:
class Program
{
public event Action<Program> SomeEvent;
static void Main(string[] args)
{
var test = typeof(Program).GetMembers().First((mi) => mi.Name == "SomeEvent");
Console.WriteLine(test.GetType());
}
}
or
class Program
{
public event Action<Program> SomeEvent;
static void Main(string[] args)
{
var test = typeof(Program).GetMembers().First((mi) => {return mi.Name == "SomeEvent";});
Console.WriteLine(test.GetType());
}
}
Both produce a result of base class MemberInfo
since that is what GetMember()
returns, with an actual type of RuntimeEventInfo
, which is output to the console.
Does this example code differ from the actual code in some significant way?
精彩评论