【问题标题】:Lambda Generic Expression w/ Out Parameter带输出参数的 Lambda 通用表达式
【发布时间】:2013-06-22 07:17:34
【问题描述】:

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

这是我目前所拥有的:问题是.. 对于 lambda 和常规方法,我如何获得类似于 foo.Method.Name 的期望?

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

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&lt;T, TR&gt;()
  2. Func&lt;T, TR&gt;(T foo)
  3. Func&lt;T, TR&gt;(out T foo)

示例: Lambda 表达式是无名的。它将显示类似 的内容。

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

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...

【问题讨论】:

  • 我要装满的容器。刷新页面..我更新了问题。如您所见,我将其跟踪到调用调用并引发异常的方法。似乎坚持在扩展 ToExpression 中......我可以尝试在那里捕捉一些东西,看看我是否得到更多信息,但这并不重要,因为我正在赶上它一帧。重点是使用 foo.GetName(); 获取 lambda / delegates inline 和普通方法的方法名称
  • 如果您只是 return (out T t) =&gt; call(out t); 而您的方法中没有其他内容 ToExpression 会发生什么?
  • GetCamera_HWInfo(out obj) 被填写,这个方法以前一直有效。类似地,现有代码在没有表达式的情况下工作......并且只有委托作为 arg。但是,我将它们全部包装起来,因为我在某个地方读到过,最好有表达式并从中获取更多信息,而不仅仅是代表.. lambda 尤其如此。 '表达式树 lambda 不能包含 out 或 ref 参数'
  • 查看我的问题的答案stackoverflow.com/questions/3146317/…
  • 您好 Rohan,感谢您的回复。我不久前读过那个帖子。您可以看到我的代码有效,并在您的线程中完成了您想要完成的工作。我担心无法从所有包装代码中获取调用方法/lambda 的名称。

标签: c# generics lambda expression-trees out


【解决方案1】:

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

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

【讨论】:

  • 是的,我自己现在才意识到这一点。那更正了我的代码。因此,在我对 Expressions 进行实验之后,它为我提供了与我什至使用它们之前完全相同的字符串名称返回值。 "RunTime [b__2d] :: 00:00:03.53" 是为这个特定的 lambda 委托显示的内容。这甚至可以做到吗,我做错了什么,还是我需要将它的字符串解析为事后的预期?
  • 你在调用原生方法吗?看起来像是某种运行时可调用的包装器。
  • 我正在调用内联的本地方法和 lambda 委托。关键是将/或包装到表达式中,因为我认为这是我阅读的内容,以便可以获取方法本身的实际名称。问题是.. 带有 的 lambda 方法将不起作用,因此假设分配给它的变量名称可以打印。由于它现在已包装,因此将返回引擎盖下的内部变量名称,而不是几帧之后的外部变量名称。
【解决方案2】:

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

表达式树只是将表达式表示为数据结构。当您通过引用传递参数时,无法按照要求表示特定的内存位置。

【讨论】:

  • 我认为任何事情都可能通过反射或类似于 BCEL 来实现。我有一个带有 out 参数的 lambda,如果你想测试它,我提供的代码可以正常工作 + 编译。至于另一个问题,是否只能创建一个附加参数并使用我希望将其命名为 lambda 表达式的任何内容一直传播,或者将其转换为本机方法并仅使用 Method.Name?跨度>
  • 在 C# 中键入为表达式树的 Lambda 不能包含 outref 参数。它们也不能包含语句、流控制或 C# 表达式类型的子集以外的任何内容。然而,表达式树 API (System.Linq.Expressions) 确实支持更高级的构造,并且能够处理 refout 参数。不过,这是一种特殊情况,除了读取、写入和通过引用传递参数之外,无法表示其他与地址相关的操作。
  • @MikeStrobel 感谢您的澄清。所以你必须“手动”做,因为 C# 不支持这种情况。
猜你喜欢
  • 2018-02-22
  • 2014-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多