使用泛型获取C#中数组的元素总和

时间:2009-07-17 23:58:55

标签: c# arrays generics

我想编写一个方法,它可以采用数值类型的任意数组,并返回startIndex和endIndex之间所有元素的总和。这就是我所拥有的:

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'

有什么方法可以强迫T只是其中一种数字类型(长,双等)?或者他们是一种更优雅的解决方法?

9 个答案:

答案 0 :(得分:11)

不,没有办法限制泛型类型参数来使用运算符,也没有好的解决方法。一个合适的接口可以是INumericIArithmetic等接口,其中包含AddSubtract等方法,由int等所有原始类型实现, long。 MS Connect中有一个5-year old feature request for that,它仍处于活动状态。最新消息是:

  

不幸的是,我们不得不削减我们在.NET Framework 4.0中解决此问题的计划。

在那之前,你将被降级为:

  • 自己使用反射 - 凌乱而且很慢
  • 使用将为您使用Reflection的任何现有包装器(例如Microsoft.VisualBasic.CompilerServices.Operators类,使用AddObjectSubtractObject等方法,这些方法使用反射并实现相应的VB语义运营商) - 易于使用,但仍然很慢
  • 您要处理的硬编码类型(即不支持用户定义类型上的重载算术运算符),并使用巨大的if (x is int) ... else if (x is double) ...语句。

答案 1 :(得分:7)

另一种方法是使用已经可用的LINQ工具,而不是自己编写。例如:

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

由于所有内置数值类型都存在Sum扩展方法,因此您不必担心编写自己的数字类型。当然,如果你的代码的“myCollection”变量已经是泛型集合类型,这将不起作用。

答案 2 :(得分:4)

答案 3 :(得分:1)

您无法约束T允许+=运算符工作的类型。这是因为.NET没有类型意味着 numeric

答案 4 :(得分:1)

解决方案位于动态关键字中。

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

这是一个名为后期绑定的概念。

答案 5 :(得分:0)

这是因为T可以是任何类型。如果THttpWebRequest,您可以为其指定0,还是可以在其上使用+ =运算符?

您可以使用

解决第一个错误
T score = default(T);

我不确定你是如何处理第二个的,因为你必须将T约束为实现+ =运算符的类型。

答案 6 :(得分:0)

Generic constraints是我能想到的唯一可能性。但是,喝醉了,我无法准确地测试它!

答案 7 :(得分:0)

我可能是傻瓜,但不会int.Parse()解决这个问题吗?

答案 8 :(得分:0)

这是我的变种

使用类型为T的二进制运算符“add”作为默认值 但是提供了为某些指定类型自定义添加功能的功能,并且仅为其余类型使用默认二进制添加 (如果没有为类型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”