1

I am trying to use expressions w/ lambda delegates to obtain the name of the calling method but it is not formatting it properly.

Here is what I have so far: Question is.. how do I get what I am to expect similar to foo.Method.Name for both lambda and regular methods?

So far, I have tried with and without expressions.. and get the same results.

< HandleAddedDevice >b__2d

    // **************************************************************************
    public delegate TResult TimerDelegateOut <T, out TResult>(out T foo);

    // **************************************************************************
    public static string GetName<T>(this Expression<T> expression) {
      var callExpression = expression.Body as MethodCallExpression;
      return callExpression != null ? callExpression.Method.Name : string.Empty;
    }

    // **************************************************************************
    public static Expression<TimerDelegateOut<T, TResult>> ToExpression<T, TResult>(this TimerDelegateOut<T, TResult> call) {
      var p1 = Expression.Parameter(typeof(T).MakeByRefType(), "value");
      MethodCallExpression methodCall = call.Target == null
          ? Expression.Call(call.Method, p1)
          : Expression.Call(Expression.Constant(call.Target), call.Method, p1);
      return Expression.Lambda<TimerDelegateOut<T, TResult>>(methodCall, p1);
    }

    // **************************************************************************
    public static Expression<Func<TResult>> ToExpression<TResult>(this Func<TResult> call) {
      MethodCallExpression methodCall = call.Target == null
        ? Expression.Call(call.Method)
        : Expression.Call(Expression.Constant(call.Target), call.Method);
      return Expression.Lambda<Func<TResult>>(methodCall);
    }

    // **************************************************************************
    public static TResult TimeFunction<T, TResult>(TimerDelegateOut<T, TResult> foo, out T bar) {
      try {
        var result = foo.ToExpression().Compile().Invoke(out bar);
        Console.WriteLine(foo.GetName());   // is OKAY
        return result;
      } catch (Exception) {
        bar = default(T);
        return default(TResult);
      }
    }

    // **************************************************************************
    public static TResult TimeFunction<TResult>(Func<TResult> foo) {
      try {
        var result = foo.ToExpression().Compile().Invoke();
        Console.WriteLine(foo.GetName());   // <-- prints "foo" ???  Not correct.
        return result;
      } catch (Exception) {
        bar = default(T);
        return default(TResult);
      }
    }

-------------
Result GetCamera_HWInfo(out Cam_HWInfo obj)
{
  obj = new Cam_HWInfo() { < fill container here > };
  return Result.cmrOk;
}


//------------
private void HandleAddedDevice() {
    ...

  Cam_HWInfo camHWInfo;
  Result result = Watchdog.TimeFunction(GetCamera_HWInfo, out camHWInfo);

    ...

  // Try this one.. I am also using.
  var connect = new Func<bool>(delegate {
    try {
      // ...
    } catch (Exception ex) {
      return false;
    }
    return true;
  });

  result = Watchdog.TimeFunction(connect);
}

//------------
// Assume OEP
static void Main(string[] args)
{
  HandleAddedDevice();
}

Here is a test driver I can show in a simple case of what I would expect. The 3x methods I need to support are:

  1. Func<T, TR>()
  2. Func<T, TR>(T foo)
  3. Func<T, TR>(out T foo)

Example: Lambda expressions are nameless. It will show up something like < No Name>.

.Method.Name is correct, but since it is a sub-method of its Parent within the calling scope, it actually is registered on the stack, as follows:

< HandleAddedDevice >b__2d

I read here that I might need to make it an expression and then Expression.Compile() to convert it to an Action (or in my case Func)?

They said it may not be possible without a compiled expression here... Maybe this will help you to explain to me where my code is a bit off in what I am trying to do.

      class Program {
        public static class ReflectionUtility {
          public static string GetPropertyName<T>(Expression<Func<T>> expression) {
            MemberExpression body = (MemberExpression) expression.Body;
            return body.Member.Name;
          }
        }

        static void Main(string[] args) {
          Func<int, bool> lambda = i => i < 5;
          Func<int, bool> del = delegate(int i) { return i < 5; };

          // Create similar expression #1.
          Expression<Func<int, bool>> expr1 = i => i < 5;
          // Compile the expression tree into executable code.
          Func<int, bool> exprC1 = expr1.Compile();

          // Invoke the method and print the output.
          Console.WriteLine("lambda(4) = {0}   :  {1} ", lambda(4), lambda.Method.Name);
          Console.WriteLine("del   (4) = {0}   :  {1} ",    del(4), del.Method.Name);
          Console.WriteLine("expr1 (4) = {0}   :  {1} ", exprC1(4), exprC1.Method.Name);
          Console.WriteLine("          =           {0}", ReflectionUtility.GetPropertyName(() => lambda));
          Console.WriteLine("          =           {0}", ReflectionUtility.GetPropertyName(() => del));

          Console.Write("Press any key to continue...");
          Console.ReadKey();
        }

OUTPUT

lambda(4) = True   :  <Main>b__0
del   (4) = True   :  <Main>b__1
expr1 (4) = True   :  lambda_method
          =           lambda
          =           del
Press any key to continue...
Community
  • 1
  • 1
Latency
  • 426
  • 3
  • 12
  • The container out I am trying to fill. Refresh the page.. I updated the question. As you can see I traced it to the method where it invokes the call and throws an exception. Seems to persist in the extension ToExpression... I can try.catch something in there to see if I get more info, but it wouldnt matter since I am catching it up one frame. The whole point is to get the name of the method for both lambda / delegates inline and normal methods using foo.GetName(); – Latency Jun 22 '13 at 07:31
  • What happens if you simply `return (out T t) => call(out t);` and nothing else in your method `ToExpression`? – Jeppe Stig Nielsen Jun 22 '13 at 07:44
  • GetCamera_HWInfo(out obj) gets filled in. This method has always worked before. Similarly, the existing code worked without Expressions.. and just the delegates as arg. However, I wrapped them all since I read somewhere it is better to have Expressions and get more info out of this and that rather than just the delegates.. lambda especially. 'An expression tree lambda can not contain an out or ref parameter' – Latency Jun 22 '13 at 07:55
  • Check out the answer to my question http://stackoverflow.com/questions/3146317/create-expression-to-invoke-method-with-out-parameter – Rohan West Jun 24 '13 at 23:19
  • Hi Rohan and thanks for responding. I read that thread awhile back. You can see my code works and does what you wanted done in your thread. I have concerns with not being able to achieve getting the name of the calling method/lambda from inside all the wrapped code. – Latency Jun 25 '13 at 20:14

2 Answers2

3

I don't think it's possible. On Compiler Error CS1951 they write:

An expression tree just represents expressions as data structures. There is no way to represent specific memory locations as is required when you pass a parameter by reference.

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
  • I thought anything might be possible with reflection or similar to BCEL. I have a lambda with out parameter and is working + compiles just fine with the code I provided if you want to test it out. As for the other issue, would it ONLY be possible to just create an additional parameter and propagate that all the way down with whatever I wish to name it for lambda expressions OR convert it to a native method and just use Method.Name? – Latency Jun 25 '13 at 19:50
  • 1
    Lambdas typed as expression trees in C# cannot contain `out` or `ref` parameters. They also cannot contain statements, flow control, or anything other than a subset of C# expression types. The expression tree API (`System.Linq.Expressions`), however, does support more advanced constructs, and is capable of handling `ref` and `out` parameters. These are a special case, though, and apart from reading, writing, and passing by-reference parameters, no other address-related operations can be represented. – Mike Strobel Jun 26 '13 at 14:03
  • @MikeStrobel Thank you for the clarification. So you have to do it "manually" because C# does not support that case. – Jeppe Stig Nielsen Jun 26 '13 at 18:23
3

It looks like everything is fine except for how you are trying to extract the method name. Try this:

public static string GetName<T>(Expression<T> field)
{
    var callExpression = field.Body as MethodCallExpression;
    return callExpression != null ? callExpression.Method.Name : string.Empty;
}
Mike Strobel
  • 25,075
  • 57
  • 69
  • Yes, I realized that just now myself. That corrected my code. So after my experiment with Expressions it is giving me the exact same return values for the string names as before I even used them. "RunTime [b__2d] :: 00:00:03.53" Is what is being displayed for this particular lambda delegate. Can this even be done, am I doing something wrong, or do I need to just string parse it to what I would expect it to be after the fact? – Latency Jun 25 '13 at 19:17
  • Are you calling a native method? Looks like some kind of runtime callable wrapper. – Mike Strobel Jun 25 '13 at 19:24
  • I am calling both native methods and lambda delegates inlined. The point was to wrap either / or into expressions since that's what I thought I read to make it possible to get the actual name of the method itself. The thing is.. lambda methods with < no name > won't work so to assume the variable name it was assigned to is OK to print. Since it is wrapped now, the internal under the hood variable name is being returned rather than the external one up a few frames. – Latency Jun 25 '13 at 19:32