如何使用N个参数定义委托函数

时间:2014-03-12 21:09:00

标签: c# .net generics delegates func

我想知道是否有办法定义具有可变数量参数的通用函数。例如,我已经定义了泛型函数:

  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个参数的函数,我应该定义相同函数的十倍吗?

谢谢!

3 个答案:

答案 0 :(得分:1)

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

例如:

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 :(得分: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;
}

答案 2 :(得分: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) ) { ... }

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

有多种方法可以做到。

相关问题