2009-07-17 33 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”