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

时间:2015-11-18 08:45:15

标签: c# lambda

我正在尝试编写一个接受lambda表达式的方法。此lambda表达式的输入参数数量将在我的代码中有所不同。输入参数的数量将决定方法体中发生的情况。

伪代码如下:

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);
}

我真的不知道foo应该接受什么对象作为参数(我猜可能是一个Expression?)而且我也不确定如何获取lambda表达式中的参数数量以及调用它

此外,如果可以通过传递具有可变数量元素的数组来调用e,那将是一个额外的好处。

附加背景:

我有一个时间序列对象。当使用N个参数调用foo时,我希望使用时间序列以及它的第1,第2,第3 ......(N-1)个导数来生成结果(例如bool)。可能存在我只需要使用时间序列本身的情况 - 在这种情况下,我只提供一个参数;在其他情况下,我希望使用时间序列生成结果,它是一阶导数以及它的二阶导数 - 在这种情况下,我将提供3个参数。等等...

我希望这更有意义。我也愿意采用更优雅的方式来实现这一要求。

3 个答案:

答案 0 :(得分:1)

嗯,它编译并运行,但我不确定我是否想在我的代码库中使用它:

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));
        }
    }
}

我也不确定它是否能解决您的实际问题。 (另外,插入关于完全没有错误检查的常见警告等)

答案 1 :(得分:0)

基于Damien's优秀答案,我重构代码以使其更有用。

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);

}

这应该合理地运作。

答案 2 :(得分:0)

我认为要使其可维护,使foo重载更有意义。此时,您可以轻松引入自定义逻辑来处理参数数量,因为您确切知道您拥有多少参数。

// 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);
}

请注意,我之前没有使用Expression类型,因为您的问题涉及直接调用它(而不是将其传递给LINQ提供程序),但同样的基本方法也适用于Expression<Func<bool>>Expression<Func<double, bool>>,...重载。