Func<> with unknown number of parameters

前端 未结 6 1514
独厮守ぢ
独厮守ぢ 2020-12-06 00:37

Consider the following pseudo code:

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


        
相关标签:
6条回答
  • 2020-12-06 01:06

    In some cases you may be able to get away with a trick like this:

    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 讨论(0)
  • 2020-12-06 01:16

    Although it is not really what asked, a simple workaround would be to define several Foo method with different number of type arguments. It is uncommon to have function with more than 6 parameters, so one could define the following method and get away with almost every use case, while staying type safe. Renan's solution could then be used for the remaining cases.

    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 讨论(0)
  • 2020-12-06 01:20

    This could become easy with lambda expressions:

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

    Then usage coul be like:

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

    Where method can be arbitrary method returning int.

    This way you can pass any number of any erguments directly through lambda.

    0 讨论(0)
  • 2020-12-06 01:22

    That's not possible. At best, you could have a delegate that also takes a variable number of arguments, and then have the delegate parse the arguments

    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);
    
    0 讨论(0)
  • 2020-12-06 01:23

    You can use Delegate with DynamicInvoke.

    With that, you don't need to handle with object[] in f.

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

    Usage:

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

    I created a fiddle showing some examples: https://dotnetfiddle.net/LdmOqo


    Note:

    If you want to use a method group, you need to use an explict casting to 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

    0 讨论(0)
  • 2020-12-06 01:24

    You could try something similar to what I posted here: https://stackoverflow.com/a/47556051/4681344

    It will allow for any number of arguments, and enforces their types.

    public delegate T ParamsAction<T>(params object[] args);
    
    TResult Foo<TResult>(ParamsAction<TResult> f)
    {
        TResult result = f();
        return result;
    }
    

    to call it, simply......

    Foo(args => MethodToCallback("Bar", 123));
    
    0 讨论(0)
提交回复
热议问题