How to reference a field by reflection

Florian picture Florian · Dec 2, 2010 · Viewed 7.6k times · Source

Sorry for the title, it's not explicit.

Further to my precedent question, I want to subscribe a method to an event object retrieved dynamically (via reflection). The object in question is a field of a Control :

public void SubscribeEvents(Control control)
{
    Type controlType = control.GetType();
    FieldInfo[] fields = controlType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

    MethodInfo method = typeof(Trace).GetMethod("WriteTrace");

    // "button1" hardcoded for the sample
    FieldInfo f = controlType.GetField("button1", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

    // "Click" hardcoded for the sample
    EventInfo eInfo = f.FieldType.GetEvent("Click");

    if (eInfo != null)
    {
        EventHandler dummyDelegate = (s, e) => WriteTrace(s, e, eInfo.Name);
        Delegate realDelegate = Delegate.CreateDelegate(eInfo.EventHandlerType, dummyDelegate.Target, dummyDelegate.Method);
        eInfo.AddEventHandler(?????, realDelegate); // How can I reference the variable button1 ???
    }
}

I don't know how to reference the variable 'button1'. I've tried something like this :

public void SubscribeEvents(Control control)
{
    Type controlType = control.GetType();
    FieldInfo[] fields = controlType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

    MethodInfo method = typeof(Trace).GetMethod("WriteTrace");

    // "button1" hardcoded for the sample
    FieldInfo f = controlType.GetField("button1", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

    // "Click" hardcoded for the sample
    EventInfo eInfo = f.FieldType.GetEvent("Click");

    Type t = f.FieldType;
    object o = Activator.CreateInstance(t);

    f.GetValue(o);

    if (eInfo != null)
    {
        EventHandler dummyDelegate = (s, e) => WriteTrace(s, e, eInfo.Name);
        Delegate realDelegate = Delegate.CreateDelegate(eInfo.EventHandlerType, dummyDelegate.Target, dummyDelegate.Method);
        eInfo.AddEventHandler(o, realDelegate); // Why can I refer to the variable button1 ???
    }
}

But I have an exception here :

        f.GetValue(o);

System.ArgumentException was unhandled Message=Field 'button1' defined on type 'WindowsFormsApplication1.Form1' is not a field on the target object which is of type 'System.Windows.Forms.Button'.

Answer

cdhowie picture cdhowie · Dec 2, 2010

That's because you're trying to create a new instance of Button and trying to get the value of its button1 property, which obviously does not exist.

Replace this:

Type t = f.FieldType;
object o = Activator.CreateInstance(t);

f.GetValue(o);

with this:

object o = f.GetValue(control);

You can use a method like this to obtain the value of a field for any object:

public static T GetFieldValue<T>(object obj, string fieldName)
{
    if (obj == null)
        throw new ArgumentNullException("obj");

    var field = obj.GetType().GetField(fieldName, BindingFlags.Public |
                                                  BindingFlags.NonPublic |
                                                  BindingFlags.Instance);

    if (field == null)
        throw new ArgumentException("fieldName", "No such field was found.");

    if (!typeof(T).IsAssignableFrom(field.FieldType))
        throw new InvalidOperationException("Field type and requested type are not compatible.");

    return (T)field.GetValue(obj);
}