Func <> с неизвестным количеством параметров

Рассмотрим следующий псевдокод:

TResult Foo<TResult>(Func<T1, T2,...,Tn, TResult> f, params object[] args)
{
    TResult result = f(args);
    return result;
}

Функция принимает Func<> с неизвестным количеством общих параметров и списком соответствующих аргументов. Можно ли написать это на С#? Как определить и назвать Foo? Как передать args в f?

Ответ 1

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

TResult Foo<TResult>(Func<object[], TResult> f, params object[] args)
{
    TResult result = f(args);
    return result;
}


Foo<int>(args =>
{
    var name = args[0] as string;
    var age = (int) args[1];

    //...

    return age;
}, arg1, arg2, arg3);

Ответ 2

Вы можете использовать Delegate с DynamicInvoke.

С этим вам не нужно обращаться с object[] в f.

TResult Foo<TResult>(Delegate f, params object[] args)
{
    var result = f.DynamicInvoke(args);
    return (TResult)Convert.ChangeType(result, typeof(TResult));
}

Использование:

Func<string, int, bool, bool> f = (name, age, active) =>
{
    if (name == "Jon" && age == 40 && active)
    {
        return true;
    }
    return false;
}; 

Foo<bool>(f,"Jon", 40, true);

Я создал скрипку, показывающую некоторые примеры: https://dotnetfiddle.net/LdmOqo


Примечание:

Если вы хотите использовать method group, вам нужно использовать выражение для выражения для Func:

public static bool Method(string name, int age)
{
    ...
}
var method = (Func<string, int, bool>)Method;
Foo<bool>(method, "Jon", 40);

Fiddle: https://dotnetfiddle.net/3ZPLsY

Ответ 3

Вы можете попробовать что-то похожее на то, что я написал здесь: fooobar.com/questions/167940/...

Это позволит использовать любое количество аргументов и применяет их типы.

public delegate T ParamsAction<T>(params object[] args);

TResult Foo<TResult>(ParamsAction<TResult> f)
{
    TResult result = f();
    return result;
}

называть это просто......

Foo(args => MethodToCallback("Bar", 123));

Ответ 4

Это может быть легко с лямбда-выражениями:

TResult Foo<Tresult>(Func<TResult> f)
{
  TResult result = f();
  return result;
}

Тогда использование может быть таким:

var result = Foo<int>(() => method(arg1, arg2, arg3));

Где method может быть произвольным методом, возвращающим int.

Таким образом, вы можете передавать любое количество любых аргументов напрямую через лямбду.

Ответ 5

В некоторых случаях вы можете уйти с трюком, подобным этому:

public static class MyClass
{
    private static T CommonWorkMethod<T>(Func<T> wishMultipleArgsFunc)
    {
        // ... do common preparation
        T returnValue = wishMultipleArgsFunc();
        // ... do common cleanup
        return returnValue;
    }

    public static int DoCommonWorkNoParams() => CommonWorkMethod<int>(ProduceIntWithNoParams);
    public static long DoCommonWorkWithLong(long p1) => CommonWorkMethod<long>(() => ProcessOneLong(p1));
    public static string DoCommonWorkWith2Params(int p1, long p2) => CommonWorkMethod<string>(() => ConvertToCollatedString(p1, p2));

    private static int ProduceIntWithNoParams() { return 5; }
}