【已更新最新开发文章,点击查看详细】

如果想要执行由表达式树表示的 .NET 代码,则必须将其转换为可执行的 IL 指令。

Lambda 表达式到函数

由于此类型映射到一个委托类型,因此 .NET 可以检查表达式,并为匹配 lambda 表达式签名的适当委托生成 IL。

对于具有任何返回类型和参数列表的 Lambda 表达式,存在这样的委托类型:该类型是由该 Lambda 表达式表示的可执行代码的目标类型。

请注意,CompileToMethod 仅在完整的桌面框架中可用,不能用于 .NET Core。

这让你可以将表达式树转换为委托对象,并拥有生成的委托的完整调试信息。

使用下面的代码将表达式转换为委托:

Expression<Func<int>> add = () => 1 + 2;
var func = add.Compile(); // 生产委托
var answer = func();      // 执行委托
Console.WriteLine(answer);

必须将其转换为正确的委托类型,以便使任何编译时工具检查参数列表或返回类型。

执行和生存期

通过调用 func() 调用该委托将执行代码。

表达式树是不可变的,且在之后编译同一表达式树将创建执行相同代码的委托。)

通过避免对 LambdaExpression.Compile() 的任何额外调用所节省的计算时间将多于执行代码(该代码确定可导致相同可执行代码的两个不同表达式树)所花费的时间

注意事项

但是,即使是执行这个简单的操作,也存在一些必须注意的事项。

必须保证作为委托的一部分的任何变量在调用 Compile 的位置处和执行结果委托时可用。

但是,如果表达式访问实现 IDisposable 的变量,则代码可能在表达式树仍保留有对象时释放该对象。

例如,此代码工作正常,因为 int 不实现 IDisposable

private static Func<int, int> CreateBoundFunc()
{
    var constant = 5; // 常量由表达式树捕获
    Expression<Func<int, int>> expression = (b) => constant + b;
    var rVal = expression.Compile();
    return rVal;
}

在稍后执行 CreateBoundFunc 返回的函数之后,可随时访问该变量。

但是,请考虑实现 IDisposable 的此(人为设计的)类:

public class Resource : IDisposable
{
    private bool isDisposed = false;
    public int Argument
    {
        get
        {
            if (!isDisposed)
                return 5;
            else throw new ObjectDisposedException("Resource");
        }
    }

    public void Dispose()
    {
        isDisposed = true;
    }
}

如果将其用于如下所示的表达式中,则在执行 Resource.Argument 属性引用的代码时将出现 ObjectDisposedException

private static Func<int, int> CreateBoundResource()
{
    using (var constant = new Resource()) // 常量由表达式树捕获
    {
        Expression<Func<int, int>> expression = (b) => constant.Argument + b;
        var rVal = expression.Compile();
        return rVal;
    }
}

(它已被释放,因为它已在 using 语句中进行声明。)

现在,在执行从此方法返回的委托时,将在执行时引发 ObjectDisposedException

出现表示编译时构造的运行时错误确实很奇怪,但这是使用表达式树时的正常现象。

定义表达式时,请谨慎访问局部变量,且在创建可由公共 API 返回的表达式树时,谨慎访问当前对象(由 this 表示)中的状态。

在它不存在的情况下,将遇到 ReferencedAssemblyNotFoundException

总结

这提供了一种机制,用于执行表达式树所表示的代码。

如果未按预期进行,那么错误也是很容易预知的,并且将在使用表达式树的任何代码的第一个测试中捕获这些错误。

 

【已更新最新开发文章,点击查看详细】

相关文章: