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>