如何定义带有N个参数的委托函数

本文关键字:参数 函数 何定义 定义 | 更新日期: 2023-09-27 17:50:08

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

  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倍的相同函数吗?

谢谢!

如何定义带有N个参数的委托函数

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

例如:

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

使用另一个泛型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;
}

如果你正在使用常规的CLR委托(例如,Func<T,TResult>),你的方法将需要匹配委托签名。您可以创建一个带有可选参数的方法:

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) ) { ... }

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

  • http://haacked.com/archive/2010/08/10/versioning-issues-with-optional-arguments.aspx/
  • http://csharpindepth.com/Articles/General/Overloading.aspx
  • http://lostechies.com/jimmybogard/2010/05/18/caveats-of-c-4-0-optional-parameters/