简体   繁体   中英

C# - Is it possible to get the owner type of a method from an Action?

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM