2014-03-12 27 views
1

我想知道是否有方法来定义具有可变数量参数的通用函数。举例来说,我已经定义了通用的功能:如何用N个参数定义委托函数

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

谢谢!

回答

1

一个简单的技巧就是使用最简单的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]); 
    } 
} 
1

使用另一通用型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; 
} 
1

如果您正在使用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)) { ... } 

应该注意的是也有陷阱,当你有涉及可选参数方法重载:

没有做到这一点的方法不止一种。