web-dev-qa-db-ja.com

パラメータの数が不明なFunc <>

次の擬似コードについて考えてみます。

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

この関数は、不明な数のジェネリックパラメーターと対応する引数のリストを持つFunc<>を受け入れます。 C#で書くことは可能ですか? Fooを定義して呼び出す方法は? argsfに渡すにはどうすればよいですか?

15
user2341923

それは可能ではありません。せいぜい、可変数の引数を取るデリゲートを作成してから、デリゲートに引数を解析させることができます。

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

DelegateDynamicInvokeと一緒に使用できます。

これにより、fobject[]で処理する必要がなくなります。

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

11
Renan Araújo

これはラムダ式で簡単になる可能性があります:

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

次に、使用法は次のようになります。

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

ここで、methodは、intを返す任意のメソッドです。

このようにして、ラムダを介して任意の数の任意の引数を直接渡すことができます。

3
Michał Turczyn

私がここに投稿したものと同様の何かを試すことができます: 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));
1
Bryan Clark

また、実際には求められていることではありません。簡単な回避策は、型引数の数が異なる複数の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);
}

...
0
Richard

これを行うための最良の(そして最もタイプセーフな)方法は、N個の実装を提供することです(ここで、Nは受け入れる最大引数数です:

B Foo<A, B>(Func<A, B> f, A a) => f(a);
C Foo<A, B, C>(Func<A, B, C> f, A a, B b) => f(a, b);
D Foo<A, B, C, D>(Func<A, B, C, D> f, A a, B b, C c) => f(a, b, c);
E Foo<A, B, C, D, E>(Func<A, B, C, D, E> f, A a, B b, C c, D d) => f(a, b, c, d);
F Foo<A, B, C, D, E, F>(Func<A, B, C, D, E, F> f, A a, B b, C c, D d, E e) => f(a, b, c, d, e);
...
..

コンパイラは、提供されたFuncと引数の数に基づいて、呼び出すオーバーロードを認識します。これはかなり一般的な手法です。 language-ext で使用して、 カリー化部分適用 などを実行しました。

0
louthster

場合によっては、次のようなトリックで逃げることができるかもしれません。

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