如何定义带有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倍的相同函数吗?
谢谢!
一个简单的技巧是只使用最简单的返回类型的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/