2014-03-12 30 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)) { ... } 

應該注意的是也有陷阱,當你有涉及可選參數方法重載:

沒有做到這一點的方法不止一種。