4

I have an asp.net web page. This is the class implementing the page:

public partial class _Default : System.Web.UI.Page
    {
        private readonly string delegateName = "DynamicHandler";

        protected void Page_Load(object sender, EventArgs e)
        {
            EventInfo evClick = btnTest.GetType().GetEvent("Click");

            Type tDelegate = evClick.EventHandlerType;

            MethodInfo method = this.GetType().GetMethod("DynamicHandler", 
                BindingFlags.NonPublic | BindingFlags.Instance);

            Delegate d = Delegate.CreateDelegate(tDelegate, this, method);

            MethodInfo addHandler = evClick.GetAddMethod();
            Object[] addHandlerArgs = { d };
            addHandler.Invoke(btnTest, addHandlerArgs);


        }

        private void DynamicHandler(object sender, EventArgs e)
        {
            throw new NotImplementedException();
        }
    }

I am trying to hook up an event handler dynamicly. For some reason method remains null and I can't figure out why. I have done this many times before and I can't figure out what I'm missing.

EDIT: I found that this.GetType() returns the type of the page ASP.default_aspx and not the actual type implementing the page. I don't really know how to get around this...

Elad Lachmi
  • 10,406
  • 13
  • 71
  • 133
  • 1
    Why aren't you just adding the handler directly? `btnTest.Click += DynamicHandler;` – Daniel Hilgarth Sep 02 '13 at 08:51
  • @DanielHilgarth - Because I get the delegate method name as a string from our administration system. – Elad Lachmi Sep 02 '13 at 08:55
  • In that case: Have you tested the sample code you showed here? Does it really reproduce your problem? Because to me, the code you showed looks good. – Daniel Hilgarth Sep 02 '13 at 08:57
  • 1
    Working fine for me. `method` has a valid `MethodInfo` – Sriram Sakthivel Sep 02 '13 at 08:58
  • Yes, it does reproduce the problem. Each and every time... Check my edit for why. – Elad Lachmi Sep 02 '13 at 09:02
  • btnTest, is it ASP Server Button? Can you check the declaration type in partial page of code behind? Also check the page directive declaration section of aspx file, the class or type name with FQN or not. – Hi10 Sep 02 '13 at 09:07
  • `this` keyword gives the current instance reference only. If you have `DynamicHandler` in some other class you need to use `typeof(SomeOtherClass)` – Sriram Sakthivel Sep 02 '13 at 09:09
  • Thank you all for your constructive comments :| – Elad Lachmi Sep 02 '13 at 09:15
  • If you want always the declaring type, use `typeof(_Default).GetMethod(...)`. If you use `GetType().GetMethod(...)`, your problem is that `GetType()` finds the actual run-time type of the current instance, and that may well be a more specialized type than `typeof(_Default)`. Always use `typeof` keyword if what you want is a "constant" type. – Jeppe Stig Nielsen Sep 02 '13 at 09:36

3 Answers3

7

For the benefit of anyone else, GetMethod() can also return null if the arguments you passed do not match the arguments of the method you are trying to find. So, for example, if you are trying to find the method:

SomeMethod(int intParam, string stringParam)

You need to pass the parameter types to GetMethod:

type.GetMethod("SomeMethod", new[] { typeof(int), typeof(string) });

Or, if you want to specify special binding flags:

type.GetMethod("SomeMethod", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(int), typeof(string) }, null);
AJ Richardson
  • 6,610
  • 1
  • 49
  • 59
4

As you've discovered in your edit, the actually ASPX page gets compiled into a class that inherits from _Default (where your code is located). So you need to make DynamicHandler (at least) protected rather than private.

Specify BindingFlags.FlattenHierarchy also:

this.GetType().GetMethod("DynamicHandler", 
            BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy);

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
  • This will work. Also, I found out that `this.GetType().BaseType` would also work for the same reason. Thank you! – Elad Lachmi Sep 02 '13 at 09:12
  • 1
    `BindingFlags.FlattenHierarchy` is ***not*** relevant for non-static members. Inherited non-private non-static members are selected by default. (If you don't want them, use `BindingFlags.DeclaredOnly`.) The `BindingFlags.FlattenHierarchy` is for inherited **`static`** members. – Jeppe Stig Nielsen Sep 02 '13 at 09:31
  • @JeppeStigNielsen - thanks. I think I confused myself a lot with trying to get this to work. – Damien_The_Unbeliever Sep 02 '13 at 09:34
4

There are two things to be aware of.

(1) When you use:

this.GetType()

or equivalently just GetType(), the type which is returned is the actual run-time type of the actual instance. Since _Default is a non-sealed class, that type might very well be a more derived (more specialized type) than _Default, i.e. some class which has _Default as its direct or indirect base class.

If what you want is always the "constant" type _Default, use the typeof keyword, so use:

typeof(_Default)

instead of GetType(). This alone would solve your problem.

(2) Even if you specify BindingFlags.NonPublic, inherited private members are not returned. With your choice of binding flags, private methods declared in the same class (the derived class) are returned, but private methods inherited from base classes are not returned. However with internal and protected members, both the ones declared in the class itself and the ones declared in the base classes are returned.

This might make some sense, since a private method is not meant to be invoked from a derived class.

Changing the access level of your DynamicHandler method from private to e.g. protected (as suggested in the other answer) would be enough to solve your problem, as inherited protected members are selected when you use BindingFlags.NonPublic.

It is still interesting that you can't get the inherited private members with BindingFlags. Related thread C#: Accessing Inherited Private Instance Members Through Reflection. It is possible to write a method that searches all base types, of course, like:

static MethodInfo GetPrivateMethod(Type type, string name)
{
    MethodInfo retVal;
    do
    {
        retVal = type.GetMethod(name, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
        if (retVal != (object)null)
            break;
        type = type.BaseType;
    } while (type != (object)null);
    return retVal;
}

That alone would also have solved your problem.

Community
  • 1
  • 1
Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181