10

I have an Action and I wonder how could I access the instance that call the method.

Exemple:

this.FindInstance(() => this.InstanceOfAClass.Method());
this.FindInstance(() => this.InstanceOfAClass2.Method());
this.FindInstance(() => this.InstanceOfAClass3.Method());



    public void FindInstance(Action action)
    {
        // The action is this.InstanceOfAClass.Method(); and I want to get the "Instance"
        // from "action"
    }

Thank you

Jean-Philippe Leclerc
  • 6,713
  • 5
  • 43
  • 66

2 Answers2

8

I think you're looking for the Delegate.Target property.

EDIT: Okay, now I see what you're after, and you need an expression tree representing the action. Then you can find the target of the method call as another expression tree, build a LambdaExpression from that, compile and execute it, and see the result:

using System;
using System.Linq.Expressions;

class Test
{
    static string someValue;

    static void Main()
    {
        someValue = "target value";

        DisplayCallTarget(() => someValue.Replace("x", "y"));
    }

    static void DisplayCallTarget(Expression<Action> action)
    {
        // TODO: *Lots* of validation
        MethodCallExpression call = (MethodCallExpression) action.Body;

        LambdaExpression targetOnly = Expression.Lambda(call.Object, null);
        Delegate compiled = targetOnly.Compile();
        object result = compiled.DynamicInvoke(null);
        Console.WriteLine(result);
    }
}

Note that this is incredibly brittle - but it should work in simple cases.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • No, Delegate.Target is the class where the action has been invoked. I want the Instance that call the method. – Jean-Philippe Leclerc Mar 23 '11 at 18:04
  • @Jean In that case I don't understand what you're after. Please provide a short but *complete* example – Jon Skeet Mar 23 '11 at 18:06
  • @Jean: The reason this isn't working for you, is because you'll wrapped the method call in a rather useless lambda. Try `FindInstance(InstanceOfAClass.Method)` (no lambda) and it will work as you desire. If you want it to work with lambda syntax, you'll need to accept a parameter of type `Expression` and walk the expression tree. – Ben Voigt Mar 23 '11 at 18:12
  • I've tried the expression. But it only gives me informations about the call and nothing about the value (the instance) – Jean-Philippe Leclerc Mar 23 '11 at 18:16
  • @Jean-Philippe: Expression trees are the way forward here. Coming up with an example now... – Jon Skeet Mar 23 '11 at 18:31
  • Where is the actual instance? (not information about the instance) – Jacob Sep 05 '15 at 12:36
  • @Jacob: Not sure what you mean. Instance of what, exactly? The delegate instance is created by the Compile call. – Jon Skeet Sep 05 '15 at 13:02
  • Do I have an access to that instance which created by the Compile call? – Jacob Sep 05 '15 at 13:13
  • For example, when I have a method such that: DoSomething(Action action), and I invoke this method like that: DoSomething(x => x.Calculate(1, 2)); Then, how can I get the instance it generates in order to invoke the Calculate method? – Jacob Sep 05 '15 at 13:18
  • @Jacob: Well within the method, the parameter would refer to the instance. If you need the instance *outside* the method, you'd either need to return it from the method or assign it to a variable before you call the method and then pass the value into the method. If that's not clear enough, I suggest you ask a new question. – Jon Skeet Sep 05 '15 at 15:14
3

Actually I don't know if you can do it in this way. Delegate class contains only two properties: Target and Method. Accessing Target won't work because you are creating a new anonymous method, so the property will return the class in which FindInstance method is called.

Try something like this instead:

FindInstance(this.MyInstance.DoSomething);

And then access the Target property as follows:

public void FindInstance(Action action)
{
    dynamic instance = action.Target;
    Console.WriteLine(instance.Property1);
}
as-cii
  • 12,819
  • 4
  • 41
  • 43