【问题标题】:Func<> with unknown number of parameters具有未知数量参数的 Func<>
【发布时间】:2014-05-15 01:47:00
【问题描述】:

考虑以下伪代码:

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

该函数接受 Func&lt;&gt; 带有未知数量的泛型参数和相应参数的列表。可以用C#写吗?如何定义和调用Foo?如何将args 传递给f

【问题讨论】:

  • 不幸的是不可能 :( 你可以通过Delegate 并使用反射来做到这一点,但我确信这不是你想要的。

标签: c# .net generics functional-programming clr


【解决方案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】:

    您可以将DelegateDynamicInvoke 一起使用。

    这样,您就不需要在f 中处理object[]

    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

    【讨论】:

    • 这真的有效吗? DynamicInvoke 是否将数组作为逗号分隔的参数反映到方法执行中?
    • 是的@GoldBishop。 DynamicInvoke 正是这样做的。在this fiddle 我展示了一些例子。查看 msdn doc DynamicInvoke 了解更多信息。
    • hmmmm...有这样的知识可以有的恶作剧:)
    • 好点@yoyo。要将此函数与方法组一起使用,您需要使用显式转换。我创建了另一个小提琴来展示你如何做到这一点:dotnetfiddle.net/qVlt47 我会更新我的 awnser 来解释它。感谢您的评论!
    • 感谢您的更新和小提琴(dotNetFiddle 很棒!)我决定找到另一种方法来解决我的问题。 (顺便说一句,Method("yoyo", 22) 肯定返回 false!;-)
    【解决方案3】:

    这可以通过 lambda 表达式变得简单:

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

    那么用法可以是:

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

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

    通过这种方式,您可以直接通过 lambda 传递任意数量的任何参数。

    【讨论】:

      【解决方案4】:

      您可以尝试类似于我在此处发布的内容: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));
      

      【讨论】:

      • 我认为应该改为ParamsFunc&lt;&gt;,否则我同意。我会在T 中使它成为协变的(“out”)。例如public delegate TResult ParamsFunc&lt;out TResult&gt;(params object[] args);
      【解决方案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; }
      }
      

      【讨论】:

        【解决方案6】:

        虽然这并不是真正的要求,但一个简单的解决方法是定义几个具有不同数量类型参数的 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);
        }
        
        ...
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-12-06
          • 1970-01-01
          • 1970-01-01
          • 2014-04-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多