I'm trying out the classic way of making a generic vector using the new static abstract abilities (like INumber<T>) that has been added to dotnet.
I am struggling to make it so the math can be abstracted, while being used for structs.
This is what I have
public interface IVec2<T> where T : INumber<T>
{
T X { get; set; }
T Y { get; set; }
public static T Dot(IVec2<T> left, IVec2<T> right) => T.Create(left.X * right.X + left.Y * right.Y);
}
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public record struct Vec2D(double X, double Y) : IVec2<double>
{
public static Vec2D operator +(Vec2D left, Vec2D right) => left with { X = left.X + right.X, Y = left.Y + right.Y };
public static Vec2D operator -(Vec2D left, Vec2D right) => left with { X = left.X - right.X, Y = left.Y - right.Y };
public static Vec2D operator *(Vec2D left, double value) => left with { X = left.X * value, Y = left.Y * value };
public static Vec2D operator *(Vec2D left, Vec2D right) => left with { X = left.X * right.X, Y = left.Y * right.Y };
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public record struct Vec2F(float X, float Y) : IVec2<float>
{
public static Vec2F operator +(Vec2F left, Vec2F right) => left with { X = left.X + right.X, Y = left.Y + right.Y };
public static Vec2F operator -(Vec2F left, Vec2F right) => left with { X = left.X - right.X, Y = left.Y - right.Y };
public static Vec2F operator *(Vec2F left, float value) => left with { X = left.X * value, Y = left.Y * value };
public static Vec2F operator *(Vec2F left, Vec2F right) => left with { X = left.X * right.X, Y = left.Y * right.Y };
}
But this is what I want
// Not sure how to make TSelf work here
public interface IVec2<T> where T : INumber<T>
{
T X { get; set; }
T Y { get; set; }
// I'm doing this wrong, I need the type to be a struct, or some kind of TSelf
public static TSelf operator +(TSelf left, TSelf right) => left with { X = left.X + right.X, Y = left.Y + right.Y };
public static TSelf operator -(TSelf left, TSelf right) => left with { X = left.X - right.X, Y = left.Y - right.Y };
public static TSelf operator *(TSelf left, T value) => left with { X = left.X * value, Y = left.Y * value };
public static TSelf operator *(TSelf left, ITSelf right) => left with { X = left.X * right.X, Y = left.Y * right.Y };
public static T Dot(IVec2<T> left, IVec2<T> right) => T.Create(left.X * right.X + left.Y * right.Y);
}
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public record struct Vec2D(double X, double Y) : IVec2<double>
{
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public record struct Vec2F(float X, float Y) : IVec2<float>
{
}
I understand the code is confusing because I have been trying to work around getting it to return structs, but it should highlight what I'm trying to do, which is:
Make it work with any INumberable
Offload the math functions into a parent interface
Make it such that it returns the same vector type for certain operations (meaning I want a function that returns itself).
As you can see, doing stuff like a dot product are easy for me to do because it returns T rather than IVec2<T>. As soon as I reach IVec2<T> then I start running into problems because I don't know how to constrain it to itself.
Is what I want possible?
Also I understand there are pitfalls with this (like if something inherits from it and has its own state, and then it doesn't get updated in one of the parent functions). Despite being a major concern in the real world, for the sake of this question, assume that will never happen.
An example of something that can begin to work would be like this
public static TSelf Abs<TSelf>(TSelf vec) where TSelf : IVec2<T>, new() =>
new() { X = T.Abs(vec.X), Y = T.Abs(vec.Y) };
but I don't know how to make this work generically for things like operator+ (and yes, there are some major problems with this, but again ignore the real world implications).
I think you want something like this.
public interface IVec2<T,TSelf> where T : INumber<T>
where TSelf : IVec2<T,TSelf>
This is actually how the built in generic math types are constructed themselves. You could probably also have it build on some of the other generic math interfaces rather than define the operators yourself (this is how types like INumber work):
public interface IVec2<T,TSelf> : IAddditionOperators<IVec2<T,TSelf>,IVec2<T,TSelf>,IVec2<T,TSelf>> //add vectors
: IMultiplyOperators<IVec2<T,TSelf>,T,IVec2<T,TSelf>> //multiply by scalar
: IMultiplyOperators<IVec2<T,TSelf>,IVec2<T,TSelf>,T> // dot product as * operator
IMultiplyOperators<IVec2<T,TSelf>,IVec2<T,TSelf>,IVec2<T,TSelf>> // cross product as * operator, obviously only use one of these
//etc
This page https://devblogs.microsoft.com/dotnet/dotnet-7-generic-math/ has a big list of all the new interfaces, which seem to all live in System.Numerics.
Can anyone tell me if there is a way with generics to limit a generic type argument T to only:
Int16
Int32
Int64
UInt16
UInt32
UInt64
I'm aware of the where keyword, but can't find an interface for only these types,
Something like:
static bool IntegerFunction<T>(T value) where T : INumeric
More than a decade later, this feature finally exists in .NET 7. The most generic interface is INumber<TSelf> rather than INumeric (in the System.Numerics namespace), and it encompasses not just integer types. To accept just integer types, consider using IBinaryInteger<TSelf> instead. To use the example of your prototypical, mystical IntegerFunction:
static bool IntegerFunction<T>(T value) where T : IBinaryInteger<T> {
return value > T.Zero;
}
Console.WriteLine(IntegerFunction(5)); // True
Console.WriteLine(IntegerFunction((sbyte)-5)); // False
Console.WriteLine(IntegerFunction((ulong)5)); // True
The (now obsolete) answer below is left as a historical perspective.
C# does not support this. Hejlsberg has described the reasons for not implementing the feature in an interview with Bruce Eckel:
And it's not clear that the added complexity is worth the small yield that you get. If something you want to do is not directly supported in the constraint system, you can do it with a factory pattern. You could have a Matrix<T>, for example, and in that Matrix you would like to define a dot product method. That of course that means you ultimately need to understand how to multiply two Ts, but you can't say that as a constraint, at least not if T is int, double, or float. But what you could do is have your Matrix take as an argument a Calculator<T>, and in Calculator<T>, have a method called multiply. You go implement that and you pass it to the Matrix.
However, this leads to fairly convoluted code, where the user has to supply their own Calculator<T> implementation, for each T that they want to use. As long as it doesn’t have to be extensible, i.e. if you just want to support a fixed number of types, such as int and double, you can get away with a relatively simple interface:
var mat = new Matrix<int>(w, h);
(Minimal implementation in a GitHub Gist.)
However, as soon as you want the user to be able to supply their own, custom types, you need to open up this implementation so that the user can supply their own Calculator instances. For instance, to instantiate a matrix that uses a custom decimal floating point implementation, DFP, you’d have to write this code:
var mat = new Matrix<DFP>(DfpCalculator.Instance, w, h);
… and implement all the members for DfpCalculator : ICalculator<DFP>.
An alternative, which unfortunately shares the same limitations, is to work with policy classes, as discussed in Sergey Shandar’s answer.
Considering the popularity of this question and the interest behind such a function I am surprised to see that there is no answer involving T4 yet.
In this sample code I will demonstrate a very simple example of how you can use the powerful templating engine to do what the compiler pretty much does behind the scenes with generics.
Instead of going through hoops and sacrificing compile-time certainty you can simply generate the function you want for every type you like and use that accordingly (at compile time!).
In order to do this:
Create a new Text Template file called GenericNumberMethodTemplate.tt.
Remove the auto-generated code (you'll keep most of it, but some isn't needed).
Add the following snippet:
<## template language="C#" #>
<## output extension=".cs" #>
<## assembly name="System.Core" #>
<# Type[] types = new[] {
typeof(Int16), typeof(Int32), typeof(Int64),
typeof(UInt16), typeof(UInt32), typeof(UInt64)
};
#>
using System;
public static class MaxMath {
<# foreach (var type in types) {
#>
public static <#= type.Name #> Max (<#= type.Name #> val1, <#= type.Name #> val2) {
return val1 > val2 ? val1 : val2;
}
<#
} #>
}
That's it. You're done now.
Saving this file will automatically compile it to this source file:
using System;
public static class MaxMath {
public static Int16 Max (Int16 val1, Int16 val2) {
return val1 > val2 ? val1 : val2;
}
public static Int32 Max (Int32 val1, Int32 val2) {
return val1 > val2 ? val1 : val2;
}
public static Int64 Max (Int64 val1, Int64 val2) {
return val1 > val2 ? val1 : val2;
}
public static UInt16 Max (UInt16 val1, UInt16 val2) {
return val1 > val2 ? val1 : val2;
}
public static UInt32 Max (UInt32 val1, UInt32 val2) {
return val1 > val2 ? val1 : val2;
}
public static UInt64 Max (UInt64 val1, UInt64 val2) {
return val1 > val2 ? val1 : val2;
}
}
In your main method you can verify that you have compile-time certainty:
namespace TTTTTest
{
class Program
{
static void Main(string[] args)
{
long val1 = 5L;
long val2 = 10L;
Console.WriteLine(MaxMath.Max(val1, val2));
Console.Read();
}
}
}
I'll get ahead of one remark: no, this is not a violation of the DRY principle. The DRY principle is there to prevent people from duplicating code in multiple places that would cause the application to become hard to maintain.
This is not at all the case here: if you want a change then you can just change the template (a single source for all your generation!) and it's done.
In order to use it with your own custom definitions, add a namespace declaration (make sure it's the same one as the one where you'll define your own implementation) to your generated code and mark the class as partial. Afterwards, add these lines to your template file so it will be included in the eventual compilation:
<## import namespace="TheNameSpaceYouWillUse" #>
<## assembly name="$(TargetPath)" #>
Let's be honest: This is pretty cool.
Disclaimer: this sample has been heavily influenced by Metaprogramming in .NET by Kevin Hazzard and Jason Bock, Manning Publications.
There's no constraint for this. It's a real issue for anyone wanting to use generics for numeric calculations.
I'd go further and say we need
static bool GenericFunction<T>(T value)
where T : operators( +, -, /, * )
Or even
static bool GenericFunction<T>(T value)
where T : Add, Subtract
Unfortunately you only have interfaces, base classes and the keywords struct (must be value-type), class (must be reference type) and new() (must have default constructor)
You could wrap the number in something else (similar to INullable<T>) like here on codeproject.
You could apply the restriction at runtime (by reflecting for the operators or checking for types) but that does lose the advantage of having the generic in the first place.
Workaround using policies:
interface INumericPolicy<T>
{
T Zero();
T Add(T a, T b);
// add more functions here, such as multiplication etc.
}
struct NumericPolicies:
INumericPolicy<int>,
INumericPolicy<long>
// add more INumericPolicy<> for different numeric types.
{
int INumericPolicy<int>.Zero() { return 0; }
long INumericPolicy<long>.Zero() { return 0; }
int INumericPolicy<int>.Add(int a, int b) { return a + b; }
long INumericPolicy<long>.Add(long a, long b) { return a + b; }
// implement all functions from INumericPolicy<> interfaces.
public static NumericPolicies Instance = new NumericPolicies();
}
Algorithms:
static class Algorithms
{
public static T Sum<P, T>(this P p, params T[] a)
where P: INumericPolicy<T>
{
var r = p.Zero();
foreach(var i in a)
{
r = p.Add(r, i);
}
return r;
}
}
Usage:
int i = NumericPolicies.Instance.Sum(1, 2, 3, 4, 5);
long l = NumericPolicies.Instance.Sum(1L, 2, 3, 4, 5);
NumericPolicies.Instance.Sum("www", "") // compile-time error.
The solution is compile-time safe. CityLizard Framework provides compiled version for .NET 4.0. The file is lib/NETFramework4.0/CityLizard.Policy.dll.
It's also available in Nuget: https://www.nuget.org/packages/CityLizard/. See CityLizard.Policy.I structure.
Beginning with C# 7.3, you can use closer approximation - the unmanaged constraint to specify that a type parameter is a non-pointer, non-nullable unmanaged type.
class SomeGeneric<T> where T : unmanaged
{
//...
}
The unmanaged constraint implies the struct constraint and can't be combined with either the struct or new() constraints.
A type is an unmanaged type if it's any of the following types:
sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool
Any enum type
Any pointer type
Any user-defined struct type that contains fields of unmanaged types only and, in C# 7.3 and earlier, is not a constructed type (a type that includes at least one type argument)
To restrict further and eliminate pointer and user-defined types that do not implement IComparable add IComparable (but enum is still derived from IComparable, so restrict enum by adding IEquatable < T >, you can go further depending on your circumstances and add additional interfaces. unmanaged allows to keep this list shorter):
class SomeGeneric<T> where T : unmanaged, IComparable, IEquatable<T>
{
//...
}
But this doesn't prevent from DateTime instantiation.
This question is a bit of a FAQ one, so I'm posting this as wiki (since I've posted similar before, but this is an older one); anyway...
What version of .NET are you using? If you are using .NET 3.5, then I have a generic operators implementation in MiscUtil (free etc).
This has methods like T Add<T>(T x, T y), and other variants for arithmetic on different types (like DateTime + TimeSpan).
Additionally, this works for all the inbuilt, lifted and bespoke operators, and caches the delegate for performance.
Some additional background on why this is tricky is here.
You may also want to know that dynamic (4.0) sort-of solves this issue indirectly too - i.e.
dynamic x = ..., y = ...
dynamic result = x + y; // does what you expect
Unfortunately you are only able to specify struct in the where clause in this instance. It does seem strange you can't specify Int16, Int32, etc. specifically but I'm sure there's some deep implementation reason underlying the decision to not permit value types in a where clause.
I guess the only solution is to do a runtime check which unfortunately prevents the problem being picked up at compile time. That'd go something like:-
static bool IntegerFunction<T>(T value) where T : struct {
if (typeof(T) != typeof(Int16) &&
typeof(T) != typeof(Int32) &&
typeof(T) != typeof(Int64) &&
typeof(T) != typeof(UInt16) &&
typeof(T) != typeof(UInt32) &&
typeof(T) != typeof(UInt64)) {
throw new ArgumentException(
string.Format("Type '{0}' is not valid.", typeof(T).ToString()));
}
// Rest of code...
}
Which is a little bit ugly I know, but at least provides the required constraints.
I'd also look into possible performance implications for this implementation, perhaps there's a faster way out there.
Probably the closest you can do is
static bool IntegerFunction<T>(T value) where T: struct
Not sure if you could do the following
static bool IntegerFunction<T>(T value) where T: struct, IComparable
, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
For something so specific, why not just have overloads for each type, the list is so short and it would possibly have less memory footprint.
This constraint exists in .Net 7.
Check out this .NET Blog post and the actual documentation.
Starting in .NET 7, you can make use of interfaces such as INumber and IFloatingPoint to create programs such as:
using System.Numerics;
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 is in the System.Numerics namespace.
There are also interfaces such as IAdditionOperators and IComparisonOperators so you can make use of specific operators generically.
Topic is old but for future readers:
This feature is tightly related to Discriminated Unions which is not implemented in C# so far. I found its issue here:
https://github.com/dotnet/csharplang/issues/113
This issue is still open and feature has been planned for C# 10
So still we have to wait a bit more, but after releasing you can do it this way:
static bool IntegerFunction<T>(T value) where T : Int16 | Int32 | Int64 | ...
There is no way to restrict templates to types, but you can define different actions based on the type. As part of a generic numeric package, I needed a generic class to add two values.
class Something<TCell>
{
internal static TCell Sum(TCell first, TCell second)
{
if (typeof(TCell) == typeof(int))
return (TCell)((object)(((int)((object)first)) + ((int)((object)second))));
if (typeof(TCell) == typeof(double))
return (TCell)((object)(((double)((object)first)) + ((double)((object)second))));
return second;
}
}
Note that the typeofs are evaluated at compile time, so the if statements would be removed by the compiler. The compiler also removes spurious casts. So Something would resolve in the compiler to
internal static int Sum(int first, int second)
{
return first + second;
}
I created a little library functionality to solve these problems:
Instead of:
public T DifficultCalculation<T>(T a, T b)
{
T result = a * b + a; // <== WILL NOT COMPILE!
return result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Should result in 8.
You could write:
public T DifficultCalculation<T>(Number<T> a, Number<T> b)
{
Number<T> result = a * b + a;
return (T)result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Results in 8.
You can find the source code here: https://codereview.stackexchange.com/questions/26022/improvement-requested-for-generic-calculator-and-generic-number
I had a similar situation where I needed to handle numeric types and strings; seems a bit of a bizarre mix but there you go.
Again, like many people I looked at constraints and came up with a bunch of interfaces that it had to support. However, a) it wasn't 100% watertight and b), anyone new looking at this long list of constraints would be immediately very confused.
So, my approach was to put all my logic into a generic method with no constraints, but to make that generic method private. I then exposed it with public methods, one explicitly handling the type I wanted to handle - to my mind, the code is clean and explicit, e.g.
public static string DoSomething(this int input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this decimal input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this double input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this string input, ...) => DoSomethingHelper(input, ...);
private static string DoSomethingHelper<T>(this T input, ....)
{
// complex logic
}
Unfortunately .NET doesn't provide a way to do that natively.
To address this issue I created the OSS library Genumerics which provides most standard numeric operations for the following built-in numeric types and their nullable equivalents with the ability to add support for other numeric types.
sbyte, byte, short, ushort, int, uint, long, ulong, float, double, decimal, and BigInteger
The performance is equivalent to a numeric type specific solution allowing you to create efficient generic numeric algorithms.
Here's an example of the code usage.
public static T Sum(T[] items)
{
T sum = Number.Zero<T>();
foreach (T item in items)
{
sum = Number.Add(sum, item);
}
return sum;
}
public static T SumAlt(T[] items)
{
// implicit conversion to Number<T>
Number<T> sum = Number.Zero<T>();
foreach (T item in items)
{
// operator support
sum += item;
}
// implicit conversion to T
return sum;
}
.NET 6 has this functionality a preview feature:
https://devblogs.microsoft.com/dotnet/preview-features-in-net-6-generic-math/#generic-math
An example from the article:
static T Add<T>(T left, T right)
where T : INumber<T>
{
return left + right;
}
INumber is an interface that implements other interfaces, such as IAdditionOperators which allows the generic + usage. This is possible now because of another preview feature which is the static abstracts in interfaces, because the + operator overload is a static method:
/// <summary>Defines a mechanism for computing the sum of two values.</summary>
/// <typeparam name="TSelf">The type that implements this interface.</typeparam>
/// <typeparam name="TOther">The type that will be added to <typeparamref name="TSelf" />.</typeparam>
/// <typeparam name="TResult">The type that contains the sum of <typeparamref name="TSelf" /> and <typeparamref name="TOther" />.</typeparam>
[RequiresPreviewFeatures(Number.PreviewFeatureMessage, Url = Number.PreviewFeatureUrl)]
public interface IAdditionOperators<TSelf, TOther, TResult>
where TSelf : IAdditionOperators<TSelf, TOther, TResult>
{
/// <summary>Adds two values together to compute their sum.</summary>
/// <param name="left">The value to which <paramref name="right" /> is added.</param>
/// <param name="right">The value which is added to <paramref name="left" />.</param>
/// <returns>The sum of <paramref name="left" /> and <paramref name="right" />.</returns>
static abstract TResult operator +(TSelf left, TOther right);
}
I was wondering the same as samjudson, why only to integers? and if that is the case, you might want to create a helper class or something like that to hold all the types you want.
If all you want are integers, don't use a generic, that is not generic; or better yet, reject any other type by checking its type.
There is no 'good' solution for this yet. However you can narrow the type argument significantly to rule out many missfits for your hypotetical 'INumeric' constraint as Haacked has shown above.
static bool IntegerFunction<T>(T value) where T: IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>, struct
{...
If you are using .NET 4.0 and later then you can just use dynamic as method argument and check in runtime that the passed dynamic argument type is numeric/integer type.
If the type of the passed dynamic is not numeric/integer type then throw exception.
An example short code that implements the idea is something like:
using System;
public class InvalidArgumentException : Exception
{
public InvalidArgumentException(string message) : base(message) {}
}
public class InvalidArgumentTypeException : InvalidArgumentException
{
public InvalidArgumentTypeException(string message) : base(message) {}
}
public class ArgumentTypeNotIntegerException : InvalidArgumentTypeException
{
public ArgumentTypeNotIntegerException(string message) : base(message) {}
}
public static class Program
{
private static bool IntegerFunction(dynamic n)
{
if (n.GetType() != typeof(Int16) &&
n.GetType() != typeof(Int32) &&
n.GetType() != typeof(Int64) &&
n.GetType() != typeof(UInt16) &&
n.GetType() != typeof(UInt32) &&
n.GetType() != typeof(UInt64))
throw new ArgumentTypeNotIntegerException("argument type is not integer type");
//code that implements IntegerFunction goes here
}
private static void Main()
{
Console.WriteLine("{0}",IntegerFunction(0)); //Compiles, no run time error and first line of output buffer is either "True" or "False" depends on the code that implements "Program.IntegerFunction" static method.
Console.WriteLine("{0}",IntegerFunction("string")); //Also compiles but it is run time error and exception of type "ArgumentTypeNotIntegerException" is thrown here.
Console.WriteLine("This is the last Console.WriteLine output"); //Never reached and executed due the run time error and the exception thrown on the second line of Program.Main static method.
}
Of course that this solution works in run time only but never in compile time.
If you want a solution that always works in compile time and never in run time then you will have to wrap the dynamic with a public struct/class whose overloaded public constructors accept arguments of the desired types only and give the struct/class appropriate name.
It makes sense that the wrapped dynamic is always private member of the class/struct and it is the only member of the struct/class and the name of the only member of the struct/class is "value".
You will also have to define and implement public methods and/or operators that work with the desired types for the private dynamic member of the class/struct if necessary.
It also makes sense that the struct/class has special/unique constructor that accepts dynamic as argument that initializes it's only private dynamic member called "value" but the modifier of this constructor is private of course.
Once the class/struct is ready define the argument's type of IntegerFunction to be that class/struct that has been defined.
An example long code that implements the idea is something like:
using System;
public struct Integer
{
private dynamic value;
private Integer(dynamic n) { this.value = n; }
public Integer(Int16 n) { this.value = n; }
public Integer(Int32 n) { this.value = n; }
public Integer(Int64 n) { this.value = n; }
public Integer(UInt16 n) { this.value = n; }
public Integer(UInt32 n) { this.value = n; }
public Integer(UInt64 n) { this.value = n; }
public Integer(Integer n) { this.value = n.value; }
public static implicit operator Int16(Integer n) { return n.value; }
public static implicit operator Int32(Integer n) { return n.value; }
public static implicit operator Int64(Integer n) { return n.value; }
public static implicit operator UInt16(Integer n) { return n.value; }
public static implicit operator UInt32(Integer n) { return n.value; }
public static implicit operator UInt64(Integer n) { return n.value; }
public static Integer operator +(Integer x, Int16 y) { return new Integer(x.value + y); }
public static Integer operator +(Integer x, Int32 y) { return new Integer(x.value + y); }
public static Integer operator +(Integer x, Int64 y) { return new Integer(x.value + y); }
public static Integer operator +(Integer x, UInt16 y) { return new Integer(x.value + y); }
public static Integer operator +(Integer x, UInt32 y) { return new Integer(x.value + y); }
public static Integer operator +(Integer x, UInt64 y) { return new Integer(x.value + y); }
public static Integer operator -(Integer x, Int16 y) { return new Integer(x.value - y); }
public static Integer operator -(Integer x, Int32 y) { return new Integer(x.value - y); }
public static Integer operator -(Integer x, Int64 y) { return new Integer(x.value - y); }
public static Integer operator -(Integer x, UInt16 y) { return new Integer(x.value - y); }
public static Integer operator -(Integer x, UInt32 y) { return new Integer(x.value - y); }
public static Integer operator -(Integer x, UInt64 y) { return new Integer(x.value - y); }
public static Integer operator *(Integer x, Int16 y) { return new Integer(x.value * y); }
public static Integer operator *(Integer x, Int32 y) { return new Integer(x.value * y); }
public static Integer operator *(Integer x, Int64 y) { return new Integer(x.value * y); }
public static Integer operator *(Integer x, UInt16 y) { return new Integer(x.value * y); }
public static Integer operator *(Integer x, UInt32 y) { return new Integer(x.value * y); }
public static Integer operator *(Integer x, UInt64 y) { return new Integer(x.value * y); }
public static Integer operator /(Integer x, Int16 y) { return new Integer(x.value / y); }
public static Integer operator /(Integer x, Int32 y) { return new Integer(x.value / y); }
public static Integer operator /(Integer x, Int64 y) { return new Integer(x.value / y); }
public static Integer operator /(Integer x, UInt16 y) { return new Integer(x.value / y); }
public static Integer operator /(Integer x, UInt32 y) { return new Integer(x.value / y); }
public static Integer operator /(Integer x, UInt64 y) { return new Integer(x.value / y); }
public static Integer operator %(Integer x, Int16 y) { return new Integer(x.value % y); }
public static Integer operator %(Integer x, Int32 y) { return new Integer(x.value % y); }
public static Integer operator %(Integer x, Int64 y) { return new Integer(x.value % y); }
public static Integer operator %(Integer x, UInt16 y) { return new Integer(x.value % y); }
public static Integer operator %(Integer x, UInt32 y) { return new Integer(x.value % y); }
public static Integer operator %(Integer x, UInt64 y) { return new Integer(x.value % y); }
public static Integer operator +(Integer x, Integer y) { return new Integer(x.value + y.value); }
public static Integer operator -(Integer x, Integer y) { return new Integer(x.value - y.value); }
public static Integer operator *(Integer x, Integer y) { return new Integer(x.value * y.value); }
public static Integer operator /(Integer x, Integer y) { return new Integer(x.value / y.value); }
public static Integer operator %(Integer x, Integer y) { return new Integer(x.value % y.value); }
public static bool operator ==(Integer x, Int16 y) { return x.value == y; }
public static bool operator !=(Integer x, Int16 y) { return x.value != y; }
public static bool operator ==(Integer x, Int32 y) { return x.value == y; }
public static bool operator !=(Integer x, Int32 y) { return x.value != y; }
public static bool operator ==(Integer x, Int64 y) { return x.value == y; }
public static bool operator !=(Integer x, Int64 y) { return x.value != y; }
public static bool operator ==(Integer x, UInt16 y) { return x.value == y; }
public static bool operator !=(Integer x, UInt16 y) { return x.value != y; }
public static bool operator ==(Integer x, UInt32 y) { return x.value == y; }
public static bool operator !=(Integer x, UInt32 y) { return x.value != y; }
public static bool operator ==(Integer x, UInt64 y) { return x.value == y; }
public static bool operator !=(Integer x, UInt64 y) { return x.value != y; }
public static bool operator ==(Integer x, Integer y) { return x.value == y.value; }
public static bool operator !=(Integer x, Integer y) { return x.value != y.value; }
public override bool Equals(object obj) { return this == (Integer)obj; }
public override int GetHashCode() { return this.value.GetHashCode(); }
public override string ToString() { return this.value.ToString(); }
public static bool operator >(Integer x, Int16 y) { return x.value > y; }
public static bool operator <(Integer x, Int16 y) { return x.value < y; }
public static bool operator >(Integer x, Int32 y) { return x.value > y; }
public static bool operator <(Integer x, Int32 y) { return x.value < y; }
public static bool operator >(Integer x, Int64 y) { return x.value > y; }
public static bool operator <(Integer x, Int64 y) { return x.value < y; }
public static bool operator >(Integer x, UInt16 y) { return x.value > y; }
public static bool operator <(Integer x, UInt16 y) { return x.value < y; }
public static bool operator >(Integer x, UInt32 y) { return x.value > y; }
public static bool operator <(Integer x, UInt32 y) { return x.value < y; }
public static bool operator >(Integer x, UInt64 y) { return x.value > y; }
public static bool operator <(Integer x, UInt64 y) { return x.value < y; }
public static bool operator >(Integer x, Integer y) { return x.value > y.value; }
public static bool operator <(Integer x, Integer y) { return x.value < y.value; }
public static bool operator >=(Integer x, Int16 y) { return x.value >= y; }
public static bool operator <=(Integer x, Int16 y) { return x.value <= y; }
public static bool operator >=(Integer x, Int32 y) { return x.value >= y; }
public static bool operator <=(Integer x, Int32 y) { return x.value <= y; }
public static bool operator >=(Integer x, Int64 y) { return x.value >= y; }
public static bool operator <=(Integer x, Int64 y) { return x.value <= y; }
public static bool operator >=(Integer x, UInt16 y) { return x.value >= y; }
public static bool operator <=(Integer x, UInt16 y) { return x.value <= y; }
public static bool operator >=(Integer x, UInt32 y) { return x.value >= y; }
public static bool operator <=(Integer x, UInt32 y) { return x.value <= y; }
public static bool operator >=(Integer x, UInt64 y) { return x.value >= y; }
public static bool operator <=(Integer x, UInt64 y) { return x.value <= y; }
public static bool operator >=(Integer x, Integer y) { return x.value >= y.value; }
public static bool operator <=(Integer x, Integer y) { return x.value <= y.value; }
public static Integer operator +(Int16 x, Integer y) { return new Integer(x + y.value); }
public static Integer operator +(Int32 x, Integer y) { return new Integer(x + y.value); }
public static Integer operator +(Int64 x, Integer y) { return new Integer(x + y.value); }
public static Integer operator +(UInt16 x, Integer y) { return new Integer(x + y.value); }
public static Integer operator +(UInt32 x, Integer y) { return new Integer(x + y.value); }
public static Integer operator +(UInt64 x, Integer y) { return new Integer(x + y.value); }
public static Integer operator -(Int16 x, Integer y) { return new Integer(x - y.value); }
public static Integer operator -(Int32 x, Integer y) { return new Integer(x - y.value); }
public static Integer operator -(Int64 x, Integer y) { return new Integer(x - y.value); }
public static Integer operator -(UInt16 x, Integer y) { return new Integer(x - y.value); }
public static Integer operator -(UInt32 x, Integer y) { return new Integer(x - y.value); }
public static Integer operator -(UInt64 x, Integer y) { return new Integer(x - y.value); }
public static Integer operator *(Int16 x, Integer y) { return new Integer(x * y.value); }
public static Integer operator *(Int32 x, Integer y) { return new Integer(x * y.value); }
public static Integer operator *(Int64 x, Integer y) { return new Integer(x * y.value); }
public static Integer operator *(UInt16 x, Integer y) { return new Integer(x * y.value); }
public static Integer operator *(UInt32 x, Integer y) { return new Integer(x * y.value); }
public static Integer operator *(UInt64 x, Integer y) { return new Integer(x * y.value); }
public static Integer operator /(Int16 x, Integer y) { return new Integer(x / y.value); }
public static Integer operator /(Int32 x, Integer y) { return new Integer(x / y.value); }
public static Integer operator /(Int64 x, Integer y) { return new Integer(x / y.value); }
public static Integer operator /(UInt16 x, Integer y) { return new Integer(x / y.value); }
public static Integer operator /(UInt32 x, Integer y) { return new Integer(x / y.value); }
public static Integer operator /(UInt64 x, Integer y) { return new Integer(x / y.value); }
public static Integer operator %(Int16 x, Integer y) { return new Integer(x % y.value); }
public static Integer operator %(Int32 x, Integer y) { return new Integer(x % y.value); }
public static Integer operator %(Int64 x, Integer y) { return new Integer(x % y.value); }
public static Integer operator %(UInt16 x, Integer y) { return new Integer(x % y.value); }
public static Integer operator %(UInt32 x, Integer y) { return new Integer(x % y.value); }
public static Integer operator %(UInt64 x, Integer y) { return new Integer(x % y.value); }
public static bool operator ==(Int16 x, Integer y) { return x == y.value; }
public static bool operator !=(Int16 x, Integer y) { return x != y.value; }
public static bool operator ==(Int32 x, Integer y) { return x == y.value; }
public static bool operator !=(Int32 x, Integer y) { return x != y.value; }
public static bool operator ==(Int64 x, Integer y) { return x == y.value; }
public static bool operator !=(Int64 x, Integer y) { return x != y.value; }
public static bool operator ==(UInt16 x, Integer y) { return x == y.value; }
public static bool operator !=(UInt16 x, Integer y) { return x != y.value; }
public static bool operator ==(UInt32 x, Integer y) { return x == y.value; }
public static bool operator !=(UInt32 x, Integer y) { return x != y.value; }
public static bool operator ==(UInt64 x, Integer y) { return x == y.value; }
public static bool operator !=(UInt64 x, Integer y) { return x != y.value; }
public static bool operator >(Int16 x, Integer y) { return x > y.value; }
public static bool operator <(Int16 x, Integer y) { return x < y.value; }
public static bool operator >(Int32 x, Integer y) { return x > y.value; }
public static bool operator <(Int32 x, Integer y) { return x < y.value; }
public static bool operator >(Int64 x, Integer y) { return x > y.value; }
public static bool operator <(Int64 x, Integer y) { return x < y.value; }
public static bool operator >(UInt16 x, Integer y) { return x > y.value; }
public static bool operator <(UInt16 x, Integer y) { return x < y.value; }
public static bool operator >(UInt32 x, Integer y) { return x > y.value; }
public static bool operator <(UInt32 x, Integer y) { return x < y.value; }
public static bool operator >(UInt64 x, Integer y) { return x > y.value; }
public static bool operator <(UInt64 x, Integer y) { return x < y.value; }
public static bool operator >=(Int16 x, Integer y) { return x >= y.value; }
public static bool operator <=(Int16 x, Integer y) { return x <= y.value; }
public static bool operator >=(Int32 x, Integer y) { return x >= y.value; }
public static bool operator <=(Int32 x, Integer y) { return x <= y.value; }
public static bool operator >=(Int64 x, Integer y) { return x >= y.value; }
public static bool operator <=(Int64 x, Integer y) { return x <= y.value; }
public static bool operator >=(UInt16 x, Integer y) { return x >= y.value; }
public static bool operator <=(UInt16 x, Integer y) { return x <= y.value; }
public static bool operator >=(UInt32 x, Integer y) { return x >= y.value; }
public static bool operator <=(UInt32 x, Integer y) { return x <= y.value; }
public static bool operator >=(UInt64 x, Integer y) { return x >= y.value; }
public static bool operator <=(UInt64 x, Integer y) { return x <= y.value; }
}
public static class Program
{
private static bool IntegerFunction(Integer n)
{
//code that implements IntegerFunction goes here
//note that there is NO code that checks the type of n in rum time, because it is NOT needed anymore
}
private static void Main()
{
Console.WriteLine("{0}",IntegerFunction(0)); //compile error: there is no overloaded METHOD for objects of type "int" and no implicit conversion from any object, including "int", to "Integer" is known.
Console.WriteLine("{0}",IntegerFunction(new Integer(0))); //both compiles and no run time error
Console.WriteLine("{0}",IntegerFunction("string")); //compile error: there is no overloaded METHOD for objects of type "string" and no implicit conversion from any object, including "string", to "Integer" is known.
Console.WriteLine("{0}",IntegerFunction(new Integer("string"))); //compile error: there is no overloaded CONSTRUCTOR for objects of type "string"
}
}
Note that in order to use dynamic in your code you must Add Reference to Microsoft.CSharp
If the version of the .NET framework is below/under/lesser than 4.0 and dynamic is undefined in that version then you will have to use object instead and do casting to the integer type, which is trouble, so I recommend that you use at least .NET 4.0 or newer if you can so you can use dynamic instead of object.
What is the point of the exercise?
As people pointed out already, you could have a non-generic function taking the largest item, and compiler will automatically convert up smaller ints for you.
static bool IntegerFunction(Int64 value) { }
If your function is on performance-critical path (very unlikely, IMO), you could provide overloads for all needed functions.
static bool IntegerFunction(Int64 value) { }
...
static bool IntegerFunction(Int16 value) { }
I would use a generic one which you could handle externaly...
/// <summary>
/// Generic object copy of the same type
/// </summary>
/// <typeparam name="T">The type of object to copy</typeparam>
/// <param name="ObjectSource">The source object to copy</param>
public T CopyObject<T>(T ObjectSource)
{
T NewObject = System.Activator.CreateInstance<T>();
foreach (PropertyInfo p in ObjectSource.GetType().GetProperties())
NewObject.GetType().GetProperty(p.Name).SetValue(NewObject, p.GetValue(ObjectSource, null), null);
return NewObject;
}
This limitation affected me when I tried to overload operators for generic types; since there was no "INumeric" constraint, and for a bevy of other reasons the good people on stackoverflow are happy to provide, operations cannot be defined on generic types.
I wanted something like
public struct Foo<T>
{
public T Value{ get; private set; }
public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
{
return new Foo<T> { Value = LHS.Value + RHS.Value; };
}
}
I have worked around this issue using .net4 dynamic runtime typing.
public struct Foo<T>
{
public T Value { get; private set; }
public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
{
return new Foo<T> { Value = LHS.Value + (dynamic)RHS.Value };
}
}
The two things about using dynamic are
Performance. All value types get boxed.
Runtime errors. You "beat" the compiler, but lose type safety. If the generic type doesn't have the operator defined, an exception will be thrown during execution.
The .NET numeric primitive types do not share any common interface that would allow them to be used for calculations. It would be possible to define your own interfaces (e.g. ISignedWholeNumber) which would perform such operations, define structures which contain a single Int16, Int32, etc. and implement those interfaces, and then have methods which accept generic types constrained to ISignedWholeNumber, but having to convert numeric values to your structure types would likely be a nuisance.
An alternative approach would be to define static class Int64Converter<T> with a static property bool Available {get;}; and static delegates for Int64 GetInt64(T value), T FromInt64(Int64 value), bool TryStoreInt64(Int64 value, ref T dest). The class constructor could use be hard-coded to load delegates for known types, and possibly use Reflection to test whether type T implements methods with the proper names and signatures (in case it's something like a struct which contains an Int64 and represents a number, but has a custom ToString() method). This approach would lose the advantages associated with compile-time type-checking, but would still manage to avoid boxing operations and each type would only have to be "checked" once. After that, operations associated with that type would be replaced with a delegate dispatch.
If all you want is use one numeric type, you could consider creating something similar to an alias in C++ with using.
So instead of having the very generic
T ComputeSomething<T>(T value1, T value2) where T : INumeric { ... }
you could have
using MyNumType = System.Double;
T ComputeSomething<MyNumType>(MyNumType value1, MyNumType value2) { ... }
That might allow you to easily go from double to int or others if needed, but you wouldn't be able to use ComputeSomething with double and int in the same program.
But why not replace all double to int then? Because your method may want to use a double whether the input is double or int. The alias allows you to know exactly which variable uses the dynamic type.
All numeric types are structs which implement IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable. However, so does DateTime.
So this generic extension method is possible:
public static bool IsNumeric<T>(this T value) where T : struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable =>
typeof(T) != typeof(DateTime);
But it will fail for a struct that implements those interfaces, e.g.:
public struct Foo : IComparable, IComparable<Foo>, IConvertible, IEquatable<Foo>, IFormattable { /* ... */ }
This non-generic alternative is less performant, but guaranteed to work:
public static bool IsNumeric(this Type type) =>
type == typeof(sbyte) || type == typeof(byte) ||
type == typeof(short) || type == typeof(ushort) ||
type == typeof(int) || type == typeof(uint) ||
type == typeof(long) || type == typeof(ulong) ||
type == typeof(float) ||
type == typeof(double) ||
type == typeof(decimal);