params[]可以作为lambda表达式的参数吗?

16

最近我开始探索Lambda表达式,心中浮现了一个问题。假设我有一个需要不定数量参数的函数,我会使用params关键字来模拟这个变量数目。

我的问题是:我是否可以使用Lambda表达式进行类似的操作?例如:

Func<int[], int> foo = (params numbers[]) =>
                       {
                           int result;

                           foreach(int number in numbers)
                           {
                               result += numbers;
                           }

                           return result;
                       }

如果是这样,那么就出现了两个子问题——有没有一种“好”的方法来编写这样的表达式,而且我是否在某个时候想要编写这样的表达式?


我想知道你是否真的可以把它称为Lambda表达式,我认为它是一个匿名方法。 - Yuriy Faktorovich
1
@YuriyFaktorovich:这是一个lambda表达式。=>是lambda运算符。 - Kendall Frey
3
@Andrew,我不认为那会起作用,但是我没有测试过。你试过吗?而且,这样做并不是必要的,你可以使用纯数组 Func<int[], int> func = numbers => { /* ... */ } 并用 func(new[] { 2, 3, 5, 7, 42 }); 调用 lambda 表达式。 - Frédéric Hamidi
这段代码未经测试,但我并不是特别担心;我只是想看看使用可变数量参数的lambda表达式的概念是否可行。在研究了微软的文档后,我没有找到任何相关信息。 - Andrew Gray
1
@KendallFrey 我找到了确切的定义,它是一个语句 Lambda,而不是表达式,需要一些特定的规则,我认为这些括号内部被打破了。 - Yuriy Faktorovich
@Yuriy,你是完全正确的,语句lambda不能转换为表达式...但它们仍然是lambda :) - Frédéric Hamidi
3个回答

20

嗯,有点像。 首先,您需要定义一个自定义委托,而不是使用Func<>

public delegate int ParamsFunc (params int[] numbers);

你可以编写以下lambda表达式:

ParamsFunc sum = p => p.Sum();

并使用可变数量的参数调用它:

Console.WriteLine(sum(1, 2, 3));
Console.WriteLine(sum(1, 2, 3, 4));
Console.WriteLine(sum(1, 2, 3, 4, 5));

但说实话,坚持使用内置的 Func<> 委托更加简单明了。


3
我认为您能获得的最接近的东西是这样的:

Func<int[], int> foo = numbers[] =>
                       {
                           // logic...
                       }

var result = foo(Params.Get(1, 5, 4, 4, 36, 321, 21, 2, 0, -4));

同时拥有以下能力:

public static class Params
{
    public static T[] Get(params T[] arr)
    {
        return arr;
    }
}

但我看不出来这怎么比一个简单的 new[] {1, 5, 4, 4, ...} 更好。


1
这里有两个东西,左侧是 Func<int[], int> 泛型委托,右侧是 lambda 表达式。前者不可能实现,因为一个 Func<S, T> 委托的声明应该是:
public delegate TResult Func<in T, out TResult>(T arg); //ie no params involved

您需要自己的委托来接受params输入,如所接受的答案所示。

问题标题所涉及的后者在C#中也是不可能实现的,但有原因。

赋值表达式的左手边是一个编译时的东西(当然,如果它是dynamic,编译器就知道了),而右手边是一个运行时的东西(当然,在const的情况下除外)。编译器可以推断出左手边的类型,但只有在运行代码时才能得到右手边的值。当您键入以下内容时:

Func<int[], int> foo = ....

foo总是被视为Func<int[], int>。如果必须解密RHS,编译器将变得非常复杂。例如,如果您尝试的内容是可能的,请考虑以下情况:

Func<int[], int> foo = (params int[] numbers) =>
                   {
                       int result;

                       foreach(int number in numbers)
                       {
                           result += numbers;
                       }

                       return result;
                   };

//and later at some other place
foo = (int[] numbers) => 0;

//how would you call 'foo' now?

当您编写自己的接受params的委托时,您直接告诉编译器(即从LHS已知)。
命名方法参数支持的三个特性中,即out/refparams、可选参数,lambda表达式(甚至是早期的delegate语法)仅支持out/ref

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接