具有Out参数的Lambda通用表达式

时间:2013-06-22 07:17:35

标签: c# generics lambda expression-trees out

我正在尝试使用带有lambda委托的表达式来获取调用方法的名称,但它没有正确格式化它。

以下是我到目前为止所提出的问题:问题是......如何获得与lambda和常规方法的foo.Method.Name相似的内容?

到目前为止,我已尝试过使用和不使用表达式..并获得相同的结果。

  

< 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();
}

这是一个测试驱动程序,我可以在一个简单的例子中展示我期望的内容。我需要支持的3x方法是:

  1. Func<T, TR>()
  2. Func<T, TR>(T foo)
  3. Func<T, TR>(out T foo)
  4. 示例: Lambda表达式是无名的。它会显示像&lt;没有名字&gt;。

    .Method.Name是正确的,但由于它是调用范围内其Parent的子方法,它实际上是在堆栈上注册的,如下所示:

    &LT; HandleAddedDevice&gt; b__2d

    我读了here我可能需要把它作为一个表达式,然后将Expression.Compile()转换为Action(或者我的情况下是Func)?

    他们说没有编译后的表达式here可能无法实现......也许这会帮助你向我解释我的代码在我想要做的事情中有点偏离。

          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();
            }
    

    输出

    lambda(4) = True   :  <Main>b__0
    del   (4) = True   :  <Main>b__1
    expr1 (4) = True   :  lambda_method
              =           lambda
              =           del
    Press any key to continue...
    

2 个答案:

答案 0 :(得分:3)

我不认为这是可能的。在Compiler Error CS1951,他们写道:

  

表达式树只表示表达式作为数据结构。通过引用传递参数时,无法表示特定的内存位置。

答案 1 :(得分:3)

看起来一切都很好,除了你试图提取方法名称的方式。试试这个:

public static string GetName<T>(Expression<T> field)
{
    var callExpression = field.Body as MethodCallExpression;
    return callExpression != null ? callExpression.Method.Name : string.Empty;
}
相关问题