I recently ran across the problem, that I wanted a function to work on both doubles and integers and wondered, why there is no common interface for all number types (containing arithmetic operators and comparisons).
It would make writing functions like Math.Min
(which exist in a gazillion overloads) way more convenient.
Would introducing an additional interface be a breaking change?
Edit: I think about using this like
public T Add<T>(T a, T b) where T: INumber
{
return a+b;
}
or
public T Range<T>(T x, T min, T max) where T:INumber
{
return Max(x, Min(x, max), min);
}
Generic math is coming as a preview feature to .NET 6. Read the announcement in the .NET dev blog:
https://devblogs.microsoft.com/dotnet/preview-features-in-net-6-generic-math/
If you want to do such kind of "generic" arithmetics your option in a strongly-typed language such as C# are quite limited. Marc Gravell described the problem as follows:
.NET 2.0 introduced generics into the .NET world, which opened the door for many elegant solutions to existing problems. Generic constraints can be used to restrict the type-arguments to known interfaces etc, to ensure access to functionality - or for simple equality/inequality tests the
Comparer<T>.Default
andEqualityComparer<T>.Default
singletons implementIComparer<T>
andIEqualityComparer<T>
respectively (allowing us to sort elements for instance, without having to know anything about the "T" in question).With all this, though, there is still a big gap when it comes to operators. Because operators are declared as static methods, there is no
IMath<T>
or similar equivalent interface that all the numeric types implement; and indeed, the flexibility of operators would make this very hard to do in a meaningful way. Worse: many of the operators on primitive types don't even exist as operators; instead there are direct IL methods. To make the situation even more complex, Nullable<> demands the concept of "lifted operators", where the inner "T" describes the operators applicable to the nullable type - but this is implemented as a language feature, and is not provided by the runtime (making reflection even more fun).
However, C# 4.0 introduced the dynamic
keyword which you can use to choose the correct overload at runtime:
using System;
public class Program
{
static dynamic Min(dynamic a, dynamic b)
{
return Math.Min(a, b);
}
static void Main(string[] args)
{
int i = Min(3, 4);
double d = Min(3.0, 4.0);
}
}
You should be aware that this removes type-safety and you might get exceptions at runtime if the dynamic runtime cannot find a suitable overload to call, e.g. because you mixed types.
If you want to get type-safety you might want to have a look at the classes available in the MiscUtil library providing generic operators for basic operations.
Please note that if you are only after specific operations you actually might use the interfaces that the built-in types already implement. For example, a type-safe generic Min
function could look like this:
public static T Min<T>(params T[] values) where T : IComparable<T>
{
T min = values[0];
foreach (var item in values.Skip(1))
{
if (item.CompareTo(min) < 0)
min = item;
}
return min;
}
How to compute 2 + 2.35? Is it 4 or 4.35 or 4.349999? How interface can find the appropriate output type? You may write your own extension method and use overloading to solve a specific problem, but if we want to have an interface that handles all edge cases, then just imagine how long will be the size of the interface and as a consequence finding a useful function is practically impossible. Additionally, the interface adds overheads and numbers are usually the basis of calculation so we can't rely on slow operations.
Here is how you can write your own extension class:
public static class ExtensionNumbers
{
public static T Range<T>(this T input, T min, T max) where T : class
{
return input.Max(input.Min(max), min);
}
public static T Min<T>(this T input, params T[] param) where T : class
{
return null;
}
private static T Max<T>(this T input, params T[] number) where T : class
{
return null;
}
}
I used where T : class
just to be able to compile it.
They soon will support a common interface!
Check out this .NET Blog post.
Starting in .NET 6 (preview 7 I think), you will be able to make use of interfaces such as INumber
and IFloatingPoint
to create programs such as:
using System;
Console.WriteLine(Sum(1, 2, 3, 4, 5));
Console.WriteLine(Sum(10.541, 2.645));
Console.WriteLine(Sum(1.55f, 5, 9.41f, 7));
static T Sum<T>(params T[] numbers) where T : INumber<T>
{
T result = T.Zero;
foreach (T item in numbers)
{
result += item;
}
return result;
}
INumber
currently comes from the System.Runtime.Experimental
NuGet package. My project file for the sample above looks like
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Runtime.Experimental" Version="6.0.0-preview.7.21377.19" />
</ItemGroup>
</Project>
There are also interfaces such as IAdditionOperators
and IComparisonOperators
so you can make use of specific operators generically.
It isn't as simple as introducing an interface, as the operators available are different per type, and are not always even homogeneous (i.e. DateTime + TimeSpan => DateTime, DateTime - DateTime => TimeSpan).
At the techincal level, there may be lot of boxing etc involved here, plus regular operators are static
.
Actually, for Min
/ Max
, Comparer<T>.Default.Compare(x,y)
does pretty much everything you would hope.
For other operators: in .NET 4.0 dynamic
is of great help here:
T x = ..., y = ...;
T sum = (dynamic)x + (dynamic)y;
but otherwise "MiscUtil" has generic support for operators via the Operator
class, i.e.
T x = ..., y = ...;
T sum = Operator.Add(x, y); // actually Add<T>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With