Get sum of elements of an Array in C# using generics

风格不统一 提交于 2020-05-12 03:01:12

问题


I want to write a method which can take an arbitrary array of a numeric type and return the sum of all the elements between startIndex and endIndex. This is what I have:

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;
}

But the compilation fails with these 2 errors.

  • Cannot implicitly convert type 'int' to 'T'.
  • Operator '+=' cannot be applied to operands of type 'T' and 'T'

Is there any way I can force T to be only one of the numeric types (long, double etc.)? Or is their a more elegant way of solving this?


回答1:


No, there's no way to constrain generic type parameters to use operators, and there are no good workarounds for that, either. A proper one would be an interface such as INumeric or IArithmetic with methods such as Add, Subtract etc, implemented by all primitive types such as int and long. There is a 5-year old feature request for that in MS Connect, which is still Active. The latest word on that is:

Unfortunately, we've had to cut our plans to solve this problem in the .NET Framework 4.0.

Until then, you are relegated to either:

  • using Reflection yourself - messy and very slow
  • using any of the existing wrappers that will use Reflection for you (e.g. Microsoft.VisualBasic.CompilerServices.Operators class, with methods such as AddObject, SubtractObject etc which use reflection and implement VB semantics for the corresponding operators) - easy to use, but still very slow
  • hardcoding types you want to handle (i.e. no support for overloaded arithmetic operators on user-defined types), and using a giant if (x is int) ... else if (x is double) ... statement.



回答2:


Another approach is to use the LINQ tools that are already available, rather than writing your own. For example:

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

Since the Sum extension method exists for all of the built-in numeric types, you don't have to worry about writing your own. Of course, this won't work if your code's "myCollection" variable is already of a generic collection type.




回答3:


This approach works pretty well:

http://www.codeproject.com/KB/cs/genericnumerics.aspx




回答4:


There is no type to which you can constrain T that will allow the += operator to work. This is because .NET does not have a type that means numeric.




回答5:


The solution is in dynamic keyword.

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

It is a concept named late binding.




回答6:


This is because T could be any type. If T was a HttpWebRequest could you assign 0 to it, or could you use the += operator on it?

You can get around the first error by using

T score = default(T);

I'm not sure how you'd deal with the second because you'd have to constrain T to be types that implement a += operator.




回答7:


Generic constraints are the only possiblilty I can think of. But, being drunk, I can't exactly test this!




回答8:


I'm perhaps being stupid, but won't int.Parse() fix this problem?




回答9:


Here my variant

uses binary operator "add" of type T as default,
but offers ability to customize the add function for some specified types, and use the default binary add only for the rest
(throwing an exception at runtime if binary add isn't defined for type 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

Example:

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);

output: 15, 15.0, "15"



来源:https://stackoverflow.com/questions/1146208/get-sum-of-elements-of-an-array-in-c-sharp-using-generics

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!