Func<> 参数数量未知

问题描述 投票:0回答:6

考虑以下伪代码:

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

该函数接受

Func<>
以及未知数量的通用参数和相应参数的列表。可以用C#写吗?如何定义和调用
Foo
?如何将
args
传递给
f

c# .net generics functional-programming clr
6个回答
21
投票

您可以将

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

小提琴:https://dotnetfiddle.net/3ZPLsY


18
投票

那是不可能的。最好的情况是,您可以拥有一个也接受可变数量参数的委托,然后让委托解析参数

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

17
投票

使用 lambda 表达式,这可以变得容易:

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

那么用法可以是这样的:

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

其中

method
可以是返回
int
的任意方法。

这样你就可以直接通过 lambda 传递任意数量的任何参数。

为了支持异步代码,我们可以定义:

Task<TResult> Foo<TResult>(Func<Task<TResult>> f)
{
    return f();
}

// or with cancellation token
Task<TResult> Foo<TResult>(Func<CancellationToken, Task<TResult>> f, CancellationToken cancellationToken)
{
    return f(cancellationToken);
}

并像这样使用它:

var asyncResult = await Foo(async () => await asyncMethod(arg1, arg2, arg3));
// With cancellation token
var asyncResult = await Foo(
    async (ct) => await asyncMethod(arg1, arg2, arg3, ct), 
    cancellationToken);

2
投票

您可以尝试类似于我在这里发布的内容:https://stackoverflow.com/a/47556051/4681344

它将允许任意数量的参数,并强制执行它们的类型。

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

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

调用它,简单......

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

0
投票

在某些情况下,你可能可以使用这样的技巧:

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

-1
投票

虽然这并不是真正的要求,但一个简单的解决方法是定义多个具有不同数量类型参数的 Foo 方法。 函数具有超过 6 个参数的情况并不常见,因此可以定义以下方法并摆脱几乎所有用例,同时保持类型安全。 Renan 的解决方案可以用于其余情况。

public TResult Foo<TResult> (Func<TResult> f)
{
    return f();
}

public TResult Foo<T1, TResult>(Func<T1, TResult> f, T1 t1)
{
    return f(t1);
}

public TResult Foo<T1, T2, TResult>(Func<T1, T2, TResult> f, T1 t1, T2 t2)
{
    return f(t1, t2);
}

...
© www.soinside.com 2019 - 2024. All rights reserved.