2009-07-17 55 views
3

我想寫一個方法,它可以接受任意數值類型的數組,並返回startIndex和endIndex之間所有元素的總和。這是我的:在C#中使用泛型獲取數組元素的總和

private static T SumArrayRange<T>(T[] scores, int startIndex, int endIndex) 
{ 
    T score = 0; 
    for (int i = startIndex; i <= endIndex; i++) 
    { 
     score += scores[i]; 
    } 
    return score; 
} 

但編譯失敗,這兩個錯誤。

  • 無法將類型'int' 隱式轉換爲'T'。
  • 運算符「+ =」不能 施加到類型「T」和 「T」

是否有任何辦法可以力T是隻有數字類型(長之一,雙操作數等等。)?或者他們是解決這個問題的更優雅的方式?

回答

11

不,沒有辦法限制泛型類型參數來使用運算符,也沒有很好的解決方法。一個適當的接口可以是諸如INumericIArithmetic的接口,其中諸如AddSubtract等的方法由諸如intlong的所有基元類型實現。 MS Connect中有一個5-year old feature request for that,它仍然處於活動狀態。最新的文字是:

不幸的是,我們不得不削減我們的計劃來解決.NET Framework 4.0中的這個問題。

在那之前,你被歸結爲兩種:使用反射自己

  • - 混亂和非常緩慢
  • 使用任何會使用反射來爲你現有的包裝(如Microsoft.VisualBasic.CompilerServices.Operators類,有方法,如AddObject,SubtractObject等使用反射和實現相應的運營商的VB語義) - 易於使用,但仍然非常緩慢
  • 你想處理的硬編碼類型(即不支持ove在用戶定義的類型上加載算術運算符),並使用巨大的if (x is int) ... else if (x is double) ...聲明。
0

這是因爲T可以是任何類型。如果THttpWebRequest,你可以給它分配0,還是你可以使用+ =操作符?

你可以避開第一個錯誤使用

T score = default(T); 

我不知道你會如何應對第二,因爲你不得不限制噸至是實現一個+ =運算符類型。

1

沒有類型,你可以限制T將允許+=運營工作。這是因爲.NET沒有類型,這意味着數字

0

Generic constraints是我能想到的唯一可能性。但是,喝醉了,我不能完全測試這個!

0

我也許是愚蠢的,但不會int.Parse()修復這個問題?

7

另一種方法是使用已有的LINQ工具,而不是編寫自己的工具。例如:

var mySum = myCollection.Skip(startIndex).Take(count).Sum(); 

由於總和擴展方法存在於所有的內置數值類型,你不必擔心寫你自己的。當然,如果你的代碼的「myCollection」變量已經是一個通用的集合類型,這將不起作用。

1

解決方案是在動態關鍵字。

T score = default(T) 
for (int i = startIndex; i <= endIndex; i++) 
{ 
    score += (dynamic)scores[i]; 
} 
return score; 

這是一個名爲概念後期綁定

+0

非常優雅,但缺乏定製+爲X型,雖然這可以很容易地完成工作的能力。添加了我的變體;)不知道哪個更快;) – 2013-04-20 23:15:59

0

這裏我變種

使用二進制運算符類型T作爲默認的「添加」,
但提供定製的附加功能對於一些特定類型,並使用默認的二進制文件添加僅剩下
能力(如果二進制加法沒有爲類型T定義,則在運行時拋出一個異常)。

private static Func<T, T, T> CreateAdd<T>() 
    { 
     Func<T, T, T> addMethod = null; 
     Expression<Func<T, T, T>> addExpr = null; 

     if (typeof(T) == typeof(string)) 
     { 
      //addExpr = (Expression<Func<T, T, T>>)((a, b) => ((T)(object)((string)(object)a + (string)(object)b))); 
      //addMethod = addExpr.Compile(); 

      addMethod = (a, b) => { 
       string aa = (string)(object)a; 
       string bb = (string)(object)b; 

       double da; 
       double db; 
       double.TryParse(aa, out da); 
       double.TryParse(bb, out db); 
       double c = da + db; 

       string res = c.ToString(); 

       return (T)(object)res; 
      }; // End Delegate addMethod 
     } 
     else 
     { 
      ParameterExpression lhs = Expression.Parameter(typeof(T), "lhs"); 
      ParameterExpression rhs = Expression.Parameter(typeof(T), "rhs"); 

      addExpr = Expression<Func<T, T, T>>.Lambda<Func<T, T, T>>(
       Expression.Add(lhs, rhs), 
       new ParameterExpression[] { lhs, rhs } 
      ); 

      addMethod = addExpr.Compile(); 
     } 

     return addMethod; 
    } 



    // MvcTools.Aggregate.Functions.Sum<T>(vals); 
    public static T Sum<T>(params T[] vals) 
    { 
     T total = default(T); 

     //Enumerable.Aggregate(vals, delegate(T left, T right) { return left + right; }); 
     Func<T, T, T> addMethod = CreateAdd<T>(); 

     foreach (T val in vals) 
     { 
      total = addMethod(total, val); 
     } 

     return total; 
    } // End Function Sum 

例子:

int[] vals = new int[] { 1, 2, 3, 4, 5 }; 
int sum = MvcTools.Aggregate.Functions.Sum<int>(vals); 

double[] dvals = new double[] { 1, 2, 3, 4, 5 }; 
double dsum = MvcTools.Aggregate.Functions.Sum<double>(dvals); 

string[] strs = new string[] { "1", "2", "3", "4", "5" }; 
string str = MvcTools.Aggregate.Functions.Sum<string>(strs); 

輸出:15,15.0 「15」