【问题标题】:Expression.Lambda with Dynamic Input parameters带有动态输入参数的 Expression.Lambda
【发布时间】:2021-05-20 05:14:14
【问题描述】:

我正在使用表达式树开发动态业务规则引擎。因此,我不确定编译期间的参数数量。我必须创建动态输入参数。对于这个要求,我使用如下 Expression.Lambda,但它的抛出错误如下:

为 lambda 声明提供的参数数量不正确

我的语法可能有误。我提到了 Microsoft 网站,他们正在使用单个参数创建 Func,而不是参数数组。

即使有大约 16 个参数,它们也会像 Func< T1,T2,T3,T4,T5...T16 > https://msdn.microsoft.com/en-us/library/dd402862(v=vs.110).aspx 一样单独声明每个参数

是否有任何解决方法,而不是单独声明我们可以作为数组传递吗?

  var lambdaExpression = Expression.Lambda< Func< T[], bool>>(ruleExpression, pe).Compile();

在上述语法中,pe 是具有多个参数的ParameterExpression[] 数组。

在上述语法而不是T[] 数组中,如果我使用单个 T 元素,它的工作正常

   var lambdaExpression = Expression.Lambda< Func< T,T, bool>>(ruleExpression, pe).Compile();

但问题是在编译期间不会知道 T 的数量。

如果我在这里做错了,请纠正我。

【问题讨论】:

  • Expression.Lambda(...) 怎么样?
  • Expression.Lambda 是我用来从先前操作中形成的表达式树创建委托的 API 函数,或者作为参数传递给其他函数,我在这里没有显示,我只是使用了表达式树变量 ruleExpression 存储表达式树的位置
  • 我也试过普通的 Expression.Lamda

标签: c# api expression


【解决方案1】:

使用运行时分配的参数数量创建委托的简单示例。我认为这几乎没用(因为您必须通过DynamicInvoke 调用委托...您没有任何安全性并且您使用的是最慢的反射执行方法)。

public static Delegate CreateLambda(int num)
{
    var parameters = new ParameterExpression[num];

    for (int i = 0; i < num; i++)
    {
        parameters[i] = Expression.Parameter(typeof(int), "p" + i);
    }

    // We sum all the parameters together
    Expression sum = parameters[0];

    for (int i = 1; i < num; i++)
    {
        sum = Expression.Add(sum, parameters[i]);
    }

    Expression body = sum;

    LambdaExpression exp = Expression.Lambda(body, parameters);
    return exp.Compile();
}

Expression.Lambda 实际上会产生一个Expression&lt;Func&lt;...&gt;&gt;(或Expression&lt;Action&lt;...&gt;&gt;),其中Func&lt;...&gt; 是根据给定的参数计算的,但Expression&lt;...&gt;LambdaExpression 的子类。如果Func&lt;&gt;Action&lt;&gt;的参数过多,甚至在运行时会生成委托类型。

然后:

int num = 5;
Delegate del = CreateLambda<double>(num);

// Note that we have to convert to object the various parameters,
// because DynamicInvoke uses a object[]
object[] values = Enumerable.Range(1, num).Select(x => (object)(double)x).ToArray();
double result = (double)del.DynamicInvoke(values);

Console.WriteLine("{0}={1}", string.Join("+", values), result);

如果您想拥有Func&lt;T[], T&gt;,这是可能的(并且可能是一个更好的主意):

public static Func<T[], T> CreateLambda<T>(int num)
{
    var parameter = Expression.Parameter(typeof(T[]), "p");

    // We sum all the parameters together
    Expression sum = Expression.ArrayIndex(parameter, Expression.Constant(0));

    for (int i = 1; i < num; i++)
    {
        sum = Expression.Add(sum, Expression.ArrayIndex(parameter, Expression.Constant(i)));
    }

    Expression body = sum;

    var exp = Expression.Lambda<Func<T[], T>>(body, parameter);
    return exp.Compile();
}

您只需使用Expression.ArrayIndex()

那么例如:

int num = 5;
Func<double[], double> del = CreateLambda<double>(num);

double[] values = Enumerable.Range(1, num).Select(x => (double)x).ToArray();
double result = del(values);

Console.WriteLine("{0}={1}", string.Join("+", values), result);

【讨论】:

  • 伟大的 Xanatos 先生,非常感谢您解释事情并提供代码示例。我试试你的方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-23
  • 1970-01-01
  • 1970-01-01
  • 2013-05-18
  • 2021-08-17
  • 1970-01-01
  • 2014-10-12
相关资源
最近更新 更多