Могут ли параметры [] быть параметрами для лямбда-выражения?

Недавно я начал изучать лямбда-выражения, и на ум пришел вопрос. Скажем, у меня есть функция, которая требует неопределенного количества параметров. Я бы использовал ключевое слово params для моделирования этого переменного количества параметров.

Мой вопрос: могу ли я сделать что-то подобное с выражениями лямбда? Например:

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

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

                           return result;
                       }

Если да, то существуют два подвопроса - есть ли "хороший" способ написать такое выражение, и я бы даже захотел написать такое выражение в какой-то момент?

Ответ 1

Ну, вроде. Во-первых, вместо использования Func<> вам необходимо определить пользовательский делегат:

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

Затем вы можете написать следующую лямбду:

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<>.

Ответ 2

Ближайшая вещь, которую, я думаю, вы можете получить, будет примерно такой:

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, ...}

Ответ 3

Здесь есть две вещи: общий делегат Func<int[], int> на LHS и выражение лямбда на RHS. Первое невозможно, так как делегат Func<S, T> объявлен как:

public delegate TResult Func<in T, out TResult>(T arg); //ie no params involved

Вам нужен ваш собственный делегат, который принимает вход params, как показано в принятом ответе.

Последнее, о чем идет речь в вопросе, также невозможно в С#, , но по какой-то причине.

LHS выражения присваивания - это время компиляции (если только он dynamic конечно, но снова компилятор знает об этом), а его RHS - это время выполнения (если, конечно, в случае const s), Компилятор может сделать вывод о том, что напечатано на LHS, но он получает значения в RHS только во время выполнения, то есть при запуске кода. Когда вы наберете это:

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/ref, params, необязательный параметр, лямбда-выражения (или даже более ранний синтаксис delegate) поддерживают только out/ref.