简体   繁体   English

如何编写一个接受lambda表达式的方法,其中lambda表达式的参数数量未知?

[英]How can I write a method that accepts a lambda expression where the number of parameters to the lambda expression is unknown?

I'm trying to write a method that accepts a lambda expression. 我正在尝试编写一种接受lambda表达式的方法。 The number of input arguments to this lambda expression will vary in my code. 此lambda表达式的输入参数的数量在我的代码中将有所不同。 The number of input arguments will determine what happens in the method body. 输入参数的数量将确定方法主体中发生的情况。

Pseudo-code follows: 伪代码如下:

private void foo(Expression e)
{
   // I don't know what parameter type foo should accept -- please suggest!
   double a, b, c;

   int count = e.NumberOfArguments;

   double[] args;
   if(count == 1) args = new[]{a};
   else if(count == 2) args = new[]{ a, b };
   ... and so on...

   e.Invoke(args);
}

private void goo()
{
   // called as follows:
   foo(x => true);
   foo((x,y) => true);
   foo((x,y,z) => true);
}

I don't really know what object foo should accept as a parameter (I'm guessing probably an Expression?) and I'm also not sure how to obtain the number of parameters in the lambda expression as well as invoke it. 我真的不知道foo应该接受哪个对象作为参数(我猜可能是一个Expression?),而且我也不确定如何获取lambda表达式中的参数数量以及如何调用它。

Additionally, it would be a bonus if e could be invoked by passing an array with a variable number of elements. 另外,如果可以通过传递具有可变数量的元素的数组来调用e,则将是一个好处。

Additional context: 其他内容:

I have a time series object. 我有一个时间序列对象。 When foo is invoked with N parameters, I wish to use the time series together with it's 1st, 2nd, 3rd ... (N-1)-th derivative to generate a result (eg. bool). 当使用N个参数调用foo时,我希望将时间序列与其一阶,二阶,三阶...(N-1)次导数一起使用以生成结果(例如bool)。 There could be instances when I need to use only the time series itself -- in this case I would only supply a single parameter; 在某些情况下,我只需要使用时间序列本身,在这种情况下,我将只提供一个参数。 there are other situations where I wish to generate a result using the time series, it's first derivative as well as it's second derivative -- in this case, I would supply 3 parameters. 在其他情况下,我希望使用时间序列生成结果,它是一阶导数也是二阶导数-在这种情况下,我将提供3个参数。 And so on... 等等...

I hope that makes more sense. 我希望这更有意义。 I am also open to more elegant ways to implement this requirement. 我也愿意以更优雅的方式来实现此要求。

Well, it compiles and runs, but I'm not sure I'd want it in my codebase: 好了,它可以编译并运行,但是我不确定我是否希望在我的代码库中使用它:

using System;
using System.Linq.Expressions;
namespace ConsoleApplication1
{
    internal sealed class Program
    {
        private static void Main(string[] args)
        {
            goo();
            Console.ReadLine();
        }
        private static void foo(LambdaExpression e)
        {
            double a, b, c;
            a = 1.0;
            b = 1.0;
            c = 1.0;
            bool result = false;
            int count = e.Parameters.Count;
            if (count == 1)
            {
                result = (bool)e.Compile().DynamicInvoke(a);
            }
            else if (count == 2)
            {
                result = (bool)e.Compile().DynamicInvoke(a,b);
            }
            Console.WriteLine(result);
        }
        private static void goo()
        {
            foo((Expression<Func<double, bool>>) (x => true));
            foo((Expression<Func<double, double, bool>>) ((x, y) => true));
            foo((Expression<Func<double, double, double, bool>>) ((x, y, z) => true));
        }
    }
}

And I'm also not sure whether it'll be the solution to your actual problem. 而且我也不确定是否会解决您的实际问题。 (Also, insert usual caveats about complete lack of error checking, etc) (此外,请插入有关一般完全没有错误检查的警告,等等)

Based on Damien's excelent answer, I'd refactor the code to make it a little more useful. 基于Damien的出色回答,我将重构代码以使其更加有用。

static void Main(string[] args)
{
    TestFoo();
}

private static Tuple<bool, Exception> Foo<T>(LambdaExpression expression, out T result, params object[] parameters)
{
    bool succesful = false;
    result=default(T);
    Exception invokeException = null;

    if (expression.Parameters.Count == parameters.Length)
    {
        try
        {
            result = (T)expression.Compile().DynamicInvoke(parameters);
            succesful = true;
        }
        catch (Exception e)
        {
            invokeException = e;
        }
    }

    return new Tuple<bool, Exception>(succesful, invokeException);
}

private static void TestFoo()
{
    bool result;
    double sum;

    var succesful = Foo((Expression<Func<bool>>)(() => true), out result);
    Debug.Assert(succesful.Item1);
    Debug.Assert(result);
    Debug.Assert(succesful.Item2 == null);

    succesful = Foo((Expression<Func<double, bool>>)(x => x == 1.0), out result, 1.0);
    Debug.Assert(succesful.Item1);
    Debug.Assert(result);
    Debug.Assert(succesful.Item2 == null);

    succesful = Foo((Expression<Func<double, double, double>>)((x, y) => x + y), out sum, 2.0, 3.0);
    Debug.Assert(succesful.Item1);
    Debug.Assert(sum == 5);
    Debug.Assert(succesful.Item2 == null);

    succesful = Foo((Expression<Func<double, double, double>>)((x, y) => x + y), out sum, 2.0, new object());
    Debug.Assert(!succesful.Item1);
    Debug.Assert(succesful.Item2 is ArgumentException);

}

This should work reasonably well. 这应该工作得很好。

I think to make this maintainable, it makes more sense to make foo overloaded. 我认为,使其易于维护,使foo重载更为有意义。 And at that point, you can easily introduce custom logic for handling the number of parameters, because you know exactly how many parameters you have. 到那时,您可以轻松地引入用于处理参数数量的自定义逻辑,因为您确切知道自己有多少个参数。

// forward to main implementation
private void foo(Func<bool> e)
{
   foo((a, b, c) => e());
}

// forward to main implementation
private void foo(Func<double, bool> e)
{
   foo((a, b, c) => e(a));
}

// forward to main implementation
private void foo(Func<double, double, bool> e)
{
   foo((a, b, c) => e(a, b));
}

// the main implementation
private void foo(Func<double, double, double, bool> e)
{
   double a, b, c;
   ...
   e(a, b, c);
}

private void goo()
{
   foo(() => true);
   foo(x => true);
   foo((x,y) => true);
   foo((x,y,z) => true);
}

Note that I'm not using Expression types here since your question talks about invoking it directly (instead of eg passing it to a LINQ provider), but the same basic approach would work just as well for Expression<Func<bool>> , Expression<Func<double, bool>> , ... overloads. 请注意,由于您的问题是关于直接调用它(而不是例如将其传递给LINQ提供程序),因此我在这里不使用Expression类型,但是对于Expression<Func<bool>>Expression<Func<double, bool>> ,...重载。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM