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