Переменные параметры в С# Lambda

Возможно ли иметь С# лямбда/делегат, который может принимать переменное количество параметров, которые могут быть вызваны с помощью динамического вызова?

Все мои попытки использовать ключевое слово 'params' в этом контексте потерпели неудачу.


ОБНОВИТЬ С РАБОЧИМ КОДОМ ОТ ОТВЕТ:

delegate void Foo(params string[] strings);

static void Main(string[] args)                       
{
    Foo x = strings =>
    {
        foreach(string s in strings)
            Console.WriteLine(s);
    };

    //Added to make it clear how this eventually is used :)
    Delegate d = x;

    d.DynamicInvoke(new[]{new string[]{"1", "2", "3"}});
}

Ответы

Ответ 1

Причина, по которой это не работает при передаче аргументов непосредственно в DynamicInvoke(), состоит в том, что DynamicInvoke() ожидает массив объектов, один элемент для каждого параметра целевого метода, а компилятор будет интерпретировать один массив как массив params до DynamicInvoke() вместо одного аргумента целевому методу (если вы не сделаете его как одиночный object).

Вы также можете вызвать DynamicInvoke(), передав массив, содержащий массив параметров целевого метода. Внешний массив будет принят в качестве аргумента для параметра DynamicInvoke() single params, а внутренний массив будет принят как массив params для целевого метода.

delegate void ParamsDelegate(params object[] args);

static void Main()
{  
   ParamsDelegate paramsDelegate = x => Console.WriteLine(x.Length);

   paramsDelegate(1,2,3); //output: "3"
   paramsDelegate();      //output: "0"

   paramsDelegate.DynamicInvoke((object) new object[]{1,2,3}); //output: "3"
   paramsDelegate.DynamicInvoke((object) new object[]{}); //output: "0"

   paramsDelegate.DynamicInvoke(new []{new object[]{1,2,3}}); //output: "3"
   paramsDelegate.DynamicInvoke(new []{new object[]{}});      //output: "0"
}

Ответ 2

Нет, но любой из параметров, которые он принимает, может быть массивом.

Без подробностей, что длинный и короткий.

Ответ 3

Нет, но с небольшой помощью вы можете почти подделать его:

object[] Params(params object[] args) { return args;}

// :

Action<string, object[]> CWL = 
                  (format,  args) => Console.WriteLine(format, args);

CWL("{0}, {1}", Params(1,2));

Ответ 4

Добавляя к ответу Mark, я бы создал метод расширения для очистки бит:

static DynamicInvokeParams(this ParamsDelegate delegate, params object[] args)
{
  delegate.DynamicInvoke(new [] {args});
}

И тогда вам просто нужно сказать:

paramsDelegate.DyanamicInvokeParams(1, 2, 3);

Ответ 5

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

Но посмотрим на этот код в обновлении OP:

delegate void Foo(params string[] strings);

static void Main(string[] args)                       
{
    Foo x = strings =>
    {
        foreach(string s in strings)
            Console.WriteLine(s);
    };

    x.DynamicInvoke(new[]{new string[]{"1", "2", "3"}});
}

Вызов DynamicInvoke выше полностью лишний. Для этой последней строки было бы гораздо разумнее:

x("1", "2", "3");