c# - 检查表达式是否有自由参数

标签 c# lambda expression func

作为 funcletizer 的一部分,我希望将不包含参数的 C# 表达式替换为其计算的常量:

double d = 100.0;

Expression<Func<double, double>> ex1 = x => -x;
Expression<Func<double>> ex2 = () => -d;

Expression result;
result = Funcletize(ex1); // should return ex1 unmodified
result = Funcletize(ex2); // should return Expression.Constant(-100.0)

我知道我可以通过将表达式包装在 lambda 表达式中并调用它来计算表达式:

object result = Expression.Lambda(ex2).Compile().DynamicInvoke(); 
// result == -100

当表达式包含未绑定(bind)参数时,如上面的 ex1,这当然会失败,抛出 InvalidOperationException,因为我没有提供任何参数。 如何检查表达式是否包含此类参数?

我当前的解决方案涉及一个 try{} catch(InvoalidOperationException),但这似乎是一种非常不优雅且容易出错的方式:

// this works; by catching InvalidOperationException
public static Expression Funcletize(Expression ex)
{
    try
    {
        // Compile() will throw InvalidOperationException, 
        // if the expression contains unbound parameters
        var lambda = Expression.Lambda(ex).Compile(); 

        Object value = lambda.DynamicInvoke();
        return Expression.Constant(value, ex.Type);
    }
    catch  (InvalidOperationException)
    {
        return ex;
    }
}

最佳答案

当然,大多数事情都是可能的。这里有两件事在起作用:

  • 通过跟踪在表达式中看到的参数来删除未使用的参数
  • 评估和内联捕获的变量(强调:这是一个语义变化)——我们通过尝试识别 field->[field->]...field-> 模式来做到这一点(尽管显示的代码实际上可能执行错误-在某些情况下为阳性)

代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
static class Program
{
    static void Main()
    {
        double d = 100;

        Expression<Func<double, double>> ex1 = x => -x;
        Expression<Func<double>> ex2 = () => -d;

        var result1 = Demungify(ex1); // (x) => -x
        var result2 = Demungify(ex2); // () => -100
    }
    public static LambdaExpression Demungify(LambdaExpression ex)
    {
        var visitor = new Demungifier();
        var newBody = visitor.Visit(ex.Body);
        var args = ex.Parameters.Where(visitor.WasSeen).ToArray();
        var lambda = Expression.Lambda(newBody, args);
        if (!args.Any() && !(lambda.Body is ConstantExpression))
        {
            // evaluate that!
            object result = lambda.Compile().DynamicInvoke();
            lambda = Expression.Lambda(Expression.Constant(result, newBody.Type));    
        }
        return lambda;
    }
    class Demungifier : ExpressionVisitor
    {
        readonly HashSet<ParameterExpression> parameters = new HashSet<ParameterExpression>();

        public bool WasSeen(ParameterExpression param)
        {
            return parameters.Contains(param);
        }
        protected override Expression VisitParameter(ParameterExpression node)
        {
            parameters.Add(node);
            return base.VisitParameter(node);
        }
        protected override Expression VisitMember(MemberExpression node)
        {
            object value;
            if(TryEvaluate(node, out value))
            {
                return Expression.Constant(value, ((FieldInfo)node.Member).FieldType);
            }
            return base.VisitMember(node);
        }
        bool TryEvaluate(Expression expression, out object value)
        {
            if(expression == null)
            {
                value = null;
                return true;
            }
            if(expression.NodeType == ExpressionType.Constant)
            {
                value = ((ConstantExpression)expression).Value;
                return true;
            }
            // captured variables are always fields, potentially of fields of fields
            // eventually terminating in a ConstantExpression that is the capture-context
            MemberExpression member;
            if(expression.NodeType == ExpressionType.MemberAccess
                && (member= (MemberExpression)expression).Member.MemberType == System.Reflection.MemberTypes.Field)
            {
                object target;
                if(TryEvaluate(member.Expression, out target))
                {
                    value = ((FieldInfo)member.Member).GetValue(target);
                    return true;
                }
            }
            value = null;
            return false;
        }

    }
}

关于c# - 检查表达式是否有自由参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18716769/

相关文章:

未知方程的 Java 正则表达式

c# - 在 Stream 中找到给定字节序列开始位置的最佳方法

c# - lambda 语法中的 linq 类型声明

java - 为什么 lambda 表达式可以用作比较器?

java - 使用 lambda 删除对象

c# - 如何将 Expression<Func<BaseClass, bool>> 转换为 Expression<Func<InheritedClass, bool>>?

c# - 将日期发布到 Asp.net 核心 API

c# - 表单例份验证-密码恢复-我宁愿用户在网络上重置它而不发送电子邮件

C# WinScard.SCardListReaders 应该只列出已连接的阅读器(但它列出了所有过去安装的现在未连接的阅读器名称)

java - 用于评估 Java 对象上的逻辑表达式的库