【问题标题】:ByRef parameters with Expression trees in C#ByRef 参数与 C# 中的表达式树
【发布时间】:2011-12-14 12:37:22
【问题描述】:

如果我想创建一个表达式树,它调用带有out 参数的方法,然后返回out 值作为结果.. 我该怎么做?

以下内容不起作用(引发运行时异常),但也许最能说明我正在尝试做的事情:

private delegate void MyDelegate(out int value);
private static Func<int> Wrap(MyDelegate dele)
{
    MethodInfo fn = dele.Method;
    ParameterExpression result = ParameterExpression.Variable(typeof(int));
    BlockExpression block = BlockExpression.Block(
        typeof(int), // block result
        Expression.Call(fn, result), // hopefully result is coerced to a reference
        result); // return the variable
    return Expression.Lambda<Func<int>>(block).Compile();
}

private static void TestFunction(out int value)
{
    value = 1;
}

private static void Test()
{
    Debug.Assert(Wrap(TestFunction)() == 1);
}

我知道这可以在原始 IL 中相当容易地解决(或者实际上根本不需要运行时编译),但不幸的是,这是一个更大的表达式构建过程的一部分......所以我真的希望这不是一个限制,因为完全重写会很痛苦。

【问题讨论】:

  • Lambda 函数当然可以调用具有ref/out 参数的方法(如问题),它们不能做的是参考封闭方法的ref/out 参数。

标签: c# expression-trees


【解决方案1】:

这对我有用:

    private static Func<int> Wrap(MyDelegate dele)
    {
        var fn = dele.Method;
        var result = ParameterExpression.Variable(typeof(int));
        var block = BlockExpression.Block(
            typeof(int),
            new[] { result },
            new Expression[]
            {
                Expression.Call(fn, result),
                result,
            });
        return Expression.Lambda<Func<int>>(block).Compile();
    }

【讨论】:

    【解决方案2】:

    也许只有我一个人,但我真的不明白整件事的意义所在。要完成你想做的事情,你真的不需要写所有的东西。

    控制台应用程序中的示例代码:

        class Program
        {
            static void Main(string[] args)
            {
                var temp = Execute(DoSomething);
                Console.Write(temp);
                Console.Read();
            }
    
            static int Execute(Func<int> methodToRun)
            {
                return methodToRun.Invoke();
            }
    
            static int DoSomething()
            {
                return 1;
            }
        }
    

    如您所见,它以更简洁和干净的方式为您提供相同的结果。我认为你错过的是ActionAction&lt;&gt;Func&lt;&gt; 都是delegate 的语法糖,所以不需要混合这两种语法,也不需要像你一样重构整个表达式。

    【讨论】:

    • 练习的重点是运行时代码生成。即,如果您传递了一个完全任意的MethodInfo,并且希望能够以尽可能小的性能损失稍微调整结果/参数,该怎么办。您不能直接调用该方法,因为您在编译时不知道它的详细信息,您可以使用反射在运行时调用该方法,但这会很慢.. Expression.Compile 允许您执行第一次做更多的工作,但创建一个方法,允许您以最低的成本重复执行此操作,以供将来调用。这就是重点;)
    • 现在您已经解释过了,但是从您编写的代码来看,它看起来并不是那样的。仍然,委托 MyDelegate(out int value) 等价于 Func MyDelegate。 Func 也是 Wrap 的返回类型。对我来说,这似乎毫无意义:该方法将 Func 作为参数并输出相同的 Func 作为结果......不,仍然没有说服力:)
    • 阅读示例代码之后的部分 ;),我承认这里不需要生成运行时代码,但解释说这是一个更大问题的一部分。示例代码仅用于演示手头问题的最小、简单版本 - 包含完整代码将需要几个大型 .cs 文件。我确实考虑过制作测试用例int.TryParse(它返回一个bool 和一个out int),并要求表达式将它们组合成一个Tuple.. 但这似乎是不必要的复杂。 (也可以在不生成运行时代码的情况下解决)
    • 我明白你的意思,在过去的 2 年里,我做了很多表达式树解析和运行时代码生成。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-03
    • 1970-01-01
    • 2017-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多