2012-12-05 39 views
3

我無法從搜索中找到答案,但也許我沒有以正確的方式提問。我有一個程序,它有許多矩陣類型的函數,它們採用3D矩陣(以1D-2D鋸齒形陣列的形式)並執行元素上的函數。但是我經常需要使用相同的函數,但使用不同類型的參數:int [] [,]和float [] [,]和double [] [,]。爲多種類型的變量重載方法的更簡單方法?

到目前爲止,我只是重寫了相同的方法,但改變了類型,但我有這些東西噸,這是一個真正的痛苦,不斷重寫「重新輸入」的方法。

private float SomeFunctionA(float[][,] d) 
{ 
    float sum = 0; 
    for (int k = 0; k < d.GetLength(0); k++) 
     for (int j = 0; j < d[0].GetLength(1); j++) 
      for (int i = 0; i < d[0].GetLength(0); i++) 
       sum += d[k][i,j]; 
    return SomeFunctionB(sum); 
} 

private float SomeFunctionA(double[][,] d) 
{ 
    double sum = 0; 
    for (int k = 0; k < d.GetLength(0); k++) 
     for (int j = 0; j < d[0].GetLength(1); j++) 
      for (int i = 0; i < d[0].GetLength(0); i++) 
       sum += d[k][i,j]; 
    return SomeFunctionB(sum); 
} 

有沒有更容易的方法來允許不同類型?如果有一種方法可以使用具有功能的泛型主方法(即3 for循環和其他主體代碼),然後使用不同類型的輔助方法併爲每種情況調用泛型方法,那將是非常好的。

謝謝大家。

回答

1

有一個在C#泛型沒有運營商限制,所以我竟被下次使用:

private interface ICalculator<T> 
    { 
     T Add(T x, T y); 
     // you may want to add more operations here 
    } 

    private class Int32Calculator: ICalculator<int> 
    { 
     public int Add(int x, int y) 
     { 
      return x + y; 
     } 
    } 

    private int Int32SomeFunction(int [][,] d) 
    { 
     return SomeFunction<int>(d, new Int32Calculator()); 
    } 

    private T SomeFunction<T>(T[][,] d, ICalculator<T> calculator) 
     where T : struct 
    { 
     T sum = default(T); 
     for (int k = 0; k < d.GetLength(0); k++) 
      for (int j = 0; j < d[0].GetLength(1); j++) 
       for (int i = 0; i < d[0].GetLength(0); i++) 
        sum = calculator.Add(sum, d[k][i, j]); 
     return sum; 
    } 

對於這個解決方案可以創建幾個類,但你可以支持任何操作(不僅是+)在這種情況下。

+0

請參閱Alexei的其他解決方案,該解決方案使用委託在相當少的代碼中執行相同的操作。還要注意,正如我在答案中所說的那樣,這兩個解決方案都會增加一個相當明顯的性能成本,這對於這些低級數學運算來說可能是不可接受的。 – Servy

+0

@Servy是的我同意,但正如我所提到的'對於這種解決方案,你可以創建更多的類,但是你可以在這種情況下支持任何運算符(不僅是+)。如果你必須使用 - ,/,*運算符來Math.Abs​​函數,你將不得不通過很多聚合器。 –

+0

@Serve關於函數調用也很好。我同意這可能會傷害表現,但它依賴於使用的癡呆症的數量。 –

2

不幸的是,對於這些基本的數字類型,並不是真的。他們沒有實現的界面定義了+運算符。任何你嘗試使用反射,動態,傳遞總和/聚合函數等都會導致相當大的性能成本。由於像這樣的矩陣操作經常在性能密集的環境中進行,所以代碼的減少很少值得推廣這些方法。

+0

+1。性能考慮的好處。 –

+0

啊我明白了。那麼我在這裏提出的方法只是簡單的例子。實際上,許多功能都充滿了各種條件和對其他功能的調用。但我會檢查表現;重寫它們可能是值得的性能好處。謝謝! – superwillis

2

您可以使用泛型函數並傳遞委託來彙總和處理結果。正如Servy所說,代表的成本可能很大。你需要測量和看看下面的基本方法是否適合你。如果這些操作的性能很重要,保持單獨的版本是很好的方法。您還可以檢查使用Jon Skeet的MiscUtil對Generic operators的支持是否會給予可接受的性能。

private T SomeFunctionA<T>(T[][,] d, Func<T, T, T> aggregate, Func<T,T> postProcess) 
{ 
    T sum = default(T); 
    for (int k = 0; k < d.GetLength(0); k++) 
     for (int j = 0; j < d[0].GetLength(1); j++) 
      for (int i = 0; i < d[0].GetLength(0); i++) 
       sum = aggreagate(sum, d[k][i,j]); 
    return postProcess(sum); 
} 

aggregate(在LINQ而言,其他共同的名字「減少」)是執行原來的「總和」類似Enumerable.Aggreagate更寬泛的版本與簽名Func<TAccumulate, TSource, TAccumulate>postProcess只是摘要處理結果,代碼可以通過調用通用版本​​(如果存在)來重寫。對於原代碼的用法是:

SomeFunctionA<float>((sum, current) => sum + current, SomeFunctionB); 
+0

'SomeFunction ()'? – Zbigniew

+0

@des此方法適用於任何你需要的類型,只要你能想出一個你認爲有意義的聚合函數。它基本上只是LINQ'Aggregate',但對於3D而不是1D數據結構,並且沒有所有額外的重載類型。 – Servy

+0

嗯,現在我得到'Func 聚合'。 – Zbigniew

1

爲什麼不使用文本模板?

創建一個名爲 'Overloads.tt' 的新文件:

<#@ template debug="false" hostspecific="false" language="C#" #> 
<#@ assembly name="System.Core" #> 
<#@ import namespace="System.Linq" #> 
<#@ import namespace="System.Text" #> 
<#@ import namespace="System.Collections.Generic" #> 
<#@ output extension=".cs" #> 
using System; 

namespace MyNamespace 
{ 
    public partial class Generated 
    { 
    <# 
    var desiredTypes = new[] { "int", "float", "double" }; 
    foreach(var type in desiredTypes) { 
    #> 
    private <#=type#> SomeFunctionA(<#=type#>[][,] d) 
    { 
     <#=type#> sum = 0; 
     for (int k = 0; k < d.GetLength(0); k++) 
      for (int j = 0; j < d[0].GetLength(1); j++) 
       for (int i = 0; i < d[0].GetLength(0); i++) 
        sum += d[k][i,j]; 
     return SomeFunctionB(sum); 
    } 

    private <#=type#> SomeFunctionB(<#=type#> input) 
    { 
    return default(<#=type#>); 
    } 

    <# } #> 
    } 
} 

當你保存它,突然迸出Overloads.cs - 與您的所有方法。

這與C++模板的工作方式類似。

有兩點要注意:

var desiredTypes = new[] ....創建字符串,然後將其用於驅動其內部產生的重載方法環路的陣列。

生成類爲partial是很有意義的,所以你可以混入更傳統的代碼。

+0

哇!我不知道你可以做到這一點。絕對保存這個。謝謝 – superwillis

相關問題