【问题标题】:How define delegate function with N parameters如何定义具有 N 个参数的委托函数
【发布时间】:2014-03-12 21:09:00
【问题描述】:

我想知道是否有一种方法可以定义具有可变数量参数的泛型函数。比如我定义了泛型函数:

  public T MeasureThis<T>(Func<T> funcToMeasure)
    {
        var watch = new Stopwatch();
        watch.Start();
        T returnVal = funcToMeasure();
        watch.Stop();
        Console.WriteLine("Time elapsed: {0}", watch.Elapsed);
        return returnVal;
    }

还有:

    public T MeasureThis<T>(Func<int,int,T> funcToMeasure, int p1, int p2)
    {
        var watch = new Stopwatch();
        watch.Start();
        T returnVal = funcToMeasure(p1, p2);
        watch.Stop();
        Console.WriteLine("Time ellapsed: {0}", watch.Elapsed);
        return returnVal;
    }

我想测量函数需要执行到最后的时间。问题是要测量的函数可以没有,一,二,三,....参数。如果我想测量包含 10 个参数的函数,我应该定义 10 倍相同的函数吗?

谢谢!

【问题讨论】:

    标签: c# .net generics delegates func


    【解决方案1】:

    一个简单的技巧是只使用返回类型的最简单的Func&lt;T&gt;,而不是将其他类型作为类型参数传递给泛型,而是使用闭包从周围的上下文中捕获它们。

    例如:

    int SomeFunctionWith3Args(int arg1, int arg2, int arg3) { ... }
    

    .

    int[] arg = new int[] { 1, 2, 3 };
    var x = MeasureThis<int>(() => SomeFunctionWith3Args(arg[0], arg[1], arg[2]));
    

    如果您不熟悉这种闭包的工作原理,它基本上会创建一个新类型,其中包含您作为字段捕获的参数,并将 lambda 实现为类的方法 - 然后将调用站点替换为类的实例化和对方法的调用。例如,上面(概念上)等价于:

    int[] arg = new int[] { 1, 2, 3 };
    var closure = new TheClosure();
    closure._captured = arg;
    var x = MeasureThis<int>(closure.TheLambda());
    

    在哪里

    class TheClosure {
        public int[] _captured;
        public int TheLambda() {
            return SomeFunctionWith3Args(_captured[0], _captured[1], _captured[2]);
        }
    }
    

    【讨论】:

      【解决方案2】:

      使用另一个泛型类型 X 来表示具有许多属性的类。

      public T MeasureThis<T>(Func<X,T> funcToMeasure, X x) where X : class
      {
          var watch = new Stopwatch();
          watch.Start();
          T returnVal = funcToMeasure(x);
          watch.Stop();
          Console.WriteLine("Time ellapsed: {0}", watch.Elapsed);
          return returnVal;
      }
      

      【讨论】:

        【解决方案3】:

        如果您使用普通的 CLR 委托(例如,Func&lt;T,TResult&gt;),您的方法将需要匹配委托签名。您可以创建一个带有可选参数的方法:

        public int Foo( int x , int y = int.MinValue ) { ... }
        

        并毫无问题地将其分配给适当的代表:

        Func<int,int,int> delegateX = Foo ;
        

        (但请注意,通过委托调用第二个参数时,您将无法省略第二个参数)。但你不能这样做:

        Func<int,int> delegateY = Foo ;
        

        但是,没有什么可以阻止您创建自己的代理。您不需要使用 CLR 提供的标准委托。

        随着更多方法采用可选参数...您可以这样做:

        public TResult Foo<T1,T2,TResult>( T1 p0 , params T2[] p1 ) { ... }
        

        让您使用第二个参数的任意数量的值调用该方法。

        你可以使用重载:

        public TResult Foo<T1,T2,TResult( T1 p0 , T2 p1 , T2 p2 , T2 p3 ) { ... }
        public TResult Foo<T1,T2,TResult( T1 p0 , T2 p1 , T2 p2         ) { ... }
        public TResult Foo<T1,T2,TResult( T1 p0 , T2 p1                 ) { ... }
        

        您可能希望结合使用上述两种方法,因为使用params T[] 会产生一定的开销。这种方法让编译器可以选择最合适的重载,从而避免了构造params数组的开销:

        public TResult Foo<T1,T2,TResult>( T1 p0 , T2 p1                 ) { ... }
        public TResult Foo<T1,T2,TResult>( T1 p0 , T2 p1 , T2 p2         ) { ... }
        public TResult Foo<T1,T2,TResult>( T1 p0 , T2 p1 , T2 p2 , T2 p3 ) { ... }
        public TResult Foo<T1,T2,TResult>( T1 p0 , params T2[] p1        ) { ... }
        

        您可以使用具有默认值的方法,例如:

        public TResult Foo<T1,T2,TResult>( T1 p0 , T2 p1 = default(T2) , T2 p2 = default(T2) ) { ... }
        

        需要注意的是,当您有涉及可选参数的方法重载时会出现陷阱:

        有不止一种方法可以做到这一点。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-07-27
          • 2012-12-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多