Is it possible to template methods for any kind of integer size ?
To illustrate, imagine this very trivial example (the body of the method is not important in my question):
public int Mul(int a, int b) {
return a*b;
}
Now, I want the same method that supports any kind of integer (excluding BigInteger of course). I have to write all variants :
public long Mul(long a, long b) {
return a*b;
}
public ulong Mul(ulong a, ulong b) {
return a*b;
}
public short Mul(short a, short b) {
return a*b;
}
public ushort Mul(ushort a, ushort b) {
return a*b;
}
public byte Mul(byte a, byte b) {
return a*b;
}
While this example is very trivial and it’s not actually a problem to duplicate, if I have more complex algorithms like this (replicate for all integer kinds):
public static IEnumerable<long> GetPrimesFactors(this long number)
{
for (long i = 2; i <= number / 2; i++)
{
while (number % i == 0)
{
yield return i;
number /= i;
}
}
yield return number;
}
it introduces a maintenance risk as there is duplicated code and logic (coding integrists would say this is the evil to have same logic at multiple place).
Some of you may suggest to implements the long version and cast the result, but having to ask consumer code to cast can be confusing and reduce readability :
void SomeMethod(IEnumerable<int> valuesToProcess)
{
foreach(int value in valuesToProcess) { Console.WriteLine(value); }
}
void Main()
{
int i = 42;
SomeMethod(((long)i).GetPrimesFactors().Select(l=>(int)l));
SomeMethod(GetPrimesFactors(i));
long l = 42L;
SomeMethod(l.GetPrimesFactors().Select(l=>(int)l));
}
When I see the definition of the interface IEnumerable<T>, and especially the definitions of Sum method overloads :
public static decimal? Sum(this IEnumerable<decimal?> source);
public static decimal Sum(this IEnumerable<decimal> source);
public static double? Sum(this IEnumerable<double?> source);
public static double Sum(this IEnumerable<double> source);
public static float? Sum(this IEnumerable<float?> source);
public static float Sum(this IEnumerable<float> source);
public static int? Sum(this IEnumerable<int?> 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);
I conclude that it’s not possible… that’s why MS has to implement all overloads.
Does anyone have any tips for designing general purpose integer methods without having to duplicate logic ?
There is no clean high performance solution. The choices I can think of are:
Operatorclass, or the DLR (slow)Generic methods representing operators:
These were my first idea. The issue is how to implement them. MiscUtil does this by calling a delegate stored in a static field.
One point to note here, is that you should avoid a static constructor, since its mere existence slows down static field access.
But that involves an indirect call, and that’s expensive. I next tried to improve this by manually specializing for certain known types:
The JIT compiler is smart enough to realize which of those
ifcases it has to take, and will remove them. While that improved performance, it bloated the IL representation of the methods. And the JIT compiler is not smart enough to inline those method now, since their IL representation is long, and the inline heuristic only looks at the IL length of a method, not its machine code length. I don’t remember if these casts cause boxing, or if the JITter was smart enough to optimize that out. Still the lack of inlining is too costly.How 4) works:
First create an interface that contains the basic operations you need(arithmetic operators,…):
Implement it for each type you need with a struct:
Then make most of your actual code generic, and pass in an arithmetic helper:
And if you want a clean interface for multiple types, create a thin overloaded wrapper forwarding to the generic method:
This might be fast, because generics get specialized for each value type. It uses no indirections and the method bodies in IL don’t get too long, so inlining is possible. But I haven’t tried this myself yet.