views:

47

answers:

2

I have a class that implements an Interface with some methods. I also have method with an Action parameter where I pass the Interface method. Is it possible to get the type of the owner that has the method?

EDIT This is the wrapper:

private void Wrapper (params Action[] actions)
{
    var action = actions.FirstOrDefault();
    var type = action.Method.DeclaringType.Name;
}

private void test()
{
   Wrapper(()=> _GC.ChargeCancellation(""));
}

For demonstration purpose I don't iterate through the collection.

I want the type of _GC.

+4  A: 

EDIT: I misread the example, and thought it was using a method group conversion. You can certainly get the method in the delegate itself:

public void Wrapper(Action action)
{
    MethodInfo method = action.Method;
    Type type = method.DeclaringType; // TestClass
    string name = method.Name; // Hello
}

I'm not sure off the top of my head which method will be used if you pass in a delegate instance with multiple actions... but it shouldn't be a problem in this case.

Doh - I misread the question. When you use a lambda expression, that's going to build an extra method in the calling class - and that is the method which contains the reference to _GC.

If you don't want this behaviour, you should change Wrapper to accept params Expression<Action>[] actions - you can then examine the expression trees appropriately and find out the calls that way. It's a bit fiddly sometimes, but it's doable. Look at the Body of the expression tree, which will represent the method call. Note that if you still want to execute the actions, you can call Compile on the expression tree to obtain a delegate.

Jon Skeet
btw, congrats with the 200K barrier :)
Lasse V. Karlsen
this does not work, I get the whole WindowsForm
Rookian
@Rookian, can you paste the actual code you have in your program?
Lasse V. Karlsen
yes, I edited the question. Jon Skeet to the rescue =P
Rookian
@Rookian: Edited the answer.
Jon Skeet
Note that if you use the expression way, if your code starts becoming anything more advanced than what you've shown so far, it too will start becoming problematic. For instance, if you do this: `Wrapper(() => 1 + SomeMethodCall());` then you now have to dig beyond the + operation, etc. You need to identify the features you want implemented and write test cases for them.
Lasse V. Karlsen
I can't find a method on expression.Body to get the owner name :o Can you help me? In debugging there is a Object property where I could get the owner, but it is not available at design time
Rookian
@Rookian: You should test if the body is a MemberInvocationExpression (or whatever it's called :) - when you've got that, there should be appropriate methods on it.
Jon Skeet
thanks! MethodCallExpression is the right name
Rookian
+3  A: 

Actually, I should've spotted this to begin with but since Jon didn't either I'm not feeling too bad about it :)

The code you have to begin with in your question does not compile:

Wrapper(() => TestClass.Hello);

This is not complete code. You either have to have:

Wrapper(TestClass.Hello);
        ^
        |
        +-- notice the missing () => here

or:

Wrapper(() => TestClass.Hello());
                             ^
                             |
                             +-- notice the added parenthesis here

And now that you have edited your question, it is clear you have the second form.

There's a subtle difference between the two. Subtle to us, but important to the compiler:

Wrapper(TestClass.Hello);
        ^------+------^
               |
               +-- This is a method group

Wrapper(() => TestClass.Hello());
              ^------+--------^
                     |
                     +-- This is a method call

A method group is a reference to a method (or its overloads), a method call is executable code.

The difference to the compiler is that in the first piece of code, the compiler will wrap up the method group into an action, basically compile it like this:

Wrapper(new Action(TestClass.Hello));

and thus, you're passing that method to the Wrapper method, inside an Action delegate.

However, the second form is handled altogether differently. The compiler now produces a new method to contain your code, and then passes the new method to the Wrapper method instead of the code you had.

Thus, your code actually looks like this:

public static void Main()
{
    Wrapper(new Action(TempMethod1));
}

private static void TempMethod1()
{
    TestClass.Hello();
}

And that's why you're seeing the form class as the owner of the method, because that's what it is.

The reason I asked in a comment whether you were taking a delegate or an expression is that my brain was working at half speed. It detected something odd, but not the whole picture.

If you want to pass code like this to a method, and work with it, you have two choices:

  1. For a delegate, work with reflection, decompile the code and analyze it, find the method call and figure out which class it is made on
  2. For an expression, analyze the pieces of the expression (this requires C# 3 or above)

Since neither is trivial I'm not going to post any code here, suffice to say that what you're asking warrants a new question. Of the two, I suggest you go with the expression way if you can.

Lasse V. Karlsen
Yeah, I'd misread it as a method group conversion too.
Jon Skeet
nice =), it seems that I have to use Func for methods with parameters and return types, doesn't I? I want to pass in any method whether it has a return type or not or has additional parameters.
Rookian
Or you can simply wrap it up in a proper delegate: `Wrapper(() => delegate { TestClass.MethodThatReturnsSomething(); });`
Lasse V. Karlsen
+1 for the cool ascii explanation.
XIII