0

I've two method:

        static int SumInt(List<string> list)
        {
            return list.Select(o => int.Parse(o)).Sum();
        }

        static float SumFloat(List<string> list)
        {
            return list.Select(o => float.Parse(o)).Sum();
        }

How can i get a generic method such as static T Sum<T>(List<string> list) where T:struct{},or a method with a ValueType return: static ValueType Sum(List<string> list){} The key point is not how to convert, but how to sum a struct or ValueType.

zhusp
  • 131
  • 12
  • 2
    Generics are used when you want to generalise _infinitely many_ methods with the same logic, not just two. Does it make sense to sum _any_ value type? Not really. – Sweeper Jun 14 '21 at 03:06
  • I don't fully agree with the blanket assertion above that generics are _only_ useful when you want to apply to some arbitrary range of types rather than just two, though there is some truth to the sentiment. That said, the question of generalizing a method that relies on converting `string` values to some arbitrary `T` value has been asked and answered. See duplicate, and note particularly the accepted answer which mirrors the above comment. See highest-voted answer and others for a literal solution to your scenario. – Peter Duniho Jun 14 '21 at 03:12
  • The key point is not how to convert, but how to sum a struct or ValueType – zhusp Jun 14 '21 at 03:36
  • With https://devblogs.microsoft.com/dotnet/dotnet-7-generic-math/ you could probably sum any `IParsable` value type. – Jeremy Lakeman Jul 25 '22 at 03:54

2 Answers2

1

I would use a Dictionary<Type, Delegate> to hold the delegate that can do the computation. You then just have to register the correct delegates at run-time and it works quite easily.

private static Dictionary<Type, Delegate> _registry = new Dictionary<Type, Delegate>();

public static void Register<S, T>(Func<string, S> parse, Func<IEnumerable<S>, T> aggregate)
{
    _registry[typeof(T)] = (Func<IEnumerable<string>, T>)(xs => aggregate(xs.Select(parse)));
}

public static T Sum<T>(List<string> list) =>
    ((Func<IEnumerable<string>, T>)_registry[typeof(T)])(list);

Now I can write this code:

Register(int.Parse, Enumerable.Sum);
Register(float.Parse, Enumerable.Sum);

var sourceIntegers = new List<string>() { "1", "2", "3" };
var sourceFloats = new List<string>() { "1.1", "2.2", "3.3" };

Console.WriteLine(Sum<int>(sourceIntegers));
Console.WriteLine(Sum<float>(sourceFloats));

The output I get is this:

6
6.6

What makes this approach interesting is that I can do this:

Register(
    x => new XElement("value", x),
    xs => new XDocument(new XElement("list", xs)));
    
Console.WriteLine(Sum<XDocument>(sourceIntegers));

That outputs:

<list>
  <value>1</value>
  <value>2</value>
  <value>3</value>
</list>
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
0

From source code of IEnumerable's extension method:

        public static double? Sum(this IEnumerable<double?> source);
        public static float? Sum(this IEnumerable<float?> source);
        public static decimal Sum(this IEnumerable<decimal> source);
        public static double Sum(this IEnumerable<double> source);
        public static int Sum(this IEnumerable<int> source);
        public static float Sum(this IEnumerable<float> source);
        public static decimal? Sum(this IEnumerable<decimal?> source);
        public static int? Sum(this IEnumerable<int?> source);
        public static long? Sum(this IEnumerable<long?> source);
        public static long Sum(this IEnumerable<long> source);

You must use if else to separate different types.

        static T Sum<T>(List<string> list) where T : struct
        {
            Type t = typeof(T);
            ValueType v = 0;
            if (t == typeof(int))
            {
                v = list.Select(o => int.Parse(o)).Sum();
            }
            else if (t == typeof(float))
            {
                v = list.Select(o => float.Parse(o)).Sum();
            }
            return (T)v;
        }
zhusp
  • 131
  • 12