4

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'.

Community
  • 1
  • 1
Florian
  • 4,507
  • 10
  • 53
  • 73

1 Answers1

7

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);
}
cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • I have a problem : it works fine with button1 (and other buttons) but when it's a custom field it returns null : object o = f.GetValue(control); // if f is a fieldinfo from a field of type MyClass, o = null Whereas f has good values – Florian Dec 02 '10 at 15:51
  • @Florian: What the heck is a "custom field?" – cdhowie Dec 02 '10 at 15:52
  • @cdhowie : private MyClass _myClass; // The "custom" field (bad translation ? private System.Windows.Forms.Button button1; – Florian Dec 02 '10 at 15:57
  • Then you will need to use a `FieldInfo` object that you obtained from the type `typeof(MyClass)`. `FieldInfo` objects from one type cannot be used on unrelated types, even if they share field names. See the method that I posted -- you can use that to obtain arbitrary fields on any object. For example: `Button button = GetFieldValue – cdhowie Dec 02 '10 at 15:58
  • I have the same problem. For a button control, it's ok, but for a control of type MyClass, the line "return (T)field.GetValue(obj);" returns null – Florian Dec 03 '10 at 08:54
  • Ok, I think the problem is somewhere else. Thanks you for your help ! – Florian Dec 03 '10 at 09:16
  • @Florian: No problem! Note that if that line is returning null, it means the value stored in the field is null. If the `obj` parameter is invalid or if the `field` object doesn't reference any field on the `obj` parameter, or something else is wrong, `GetValue()` will throw an exception, not return null. – cdhowie Dec 03 '10 at 15:10