ChatGPT解决这个技术问题 Extra ChatGPT

Is there a constraint that restricts my generic method to numeric types?

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 
There are now various C# proposals that would allow accomplishing this, but AFAIK none of them are further than preliminary explorations/discussions. See Exploration: Shapes and Extensions, Exploration: Roles, extension interfaces and static interface members, Champion "Type Classes (aka Concepts, Structural Generic Constraints)", and Proposal: Generic types should support operators
As of September 2021, this PR seems to have the most traction and I think it'll be accepted for .NET 7: github.com/dotnet/designs/pull/205

K
Konrad Rudolph

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, 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, and in Calculator, 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.


btw, MiscUtil provides a generic class that does exactly this; Operator/Operator<T>; yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html
@Mark: good comment. However, just to be clear, I don't think that Hejlsberg was referring to code generation as a solution to the problem as you do in the Operator<T> code (since the interview was given long before the existence of the Expressions framework, even though one could of course use Reflection.Emit) – and I'd be really interested in his workaround.
@Konrad Rudolph: I think this answer to a similar question explains Hejlsberg's workaround. The other generic class is made abstract. Since it requires you to implement the other generic class for each type you want to support, it will result in duplicate code, but does mean you can only instantiate the original generic class with a supported type.
I do not agree to Heijsberg's phrase "So in a sense, C++ templates are actually untyped, or loosely typed. Whereas C# generics are strongly typed. ". That's really Marketing BS to promote C#. Strong/Weak-typing has not to do with quality of diagnostics. Otherwise: Interesting find.
Use decimal for everything and cast back to the type you need.
M
Mark Amery

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();
        }
    }
}

https://i.stack.imgur.com/300td.png

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.


This is pretty cool, but would it be possible to modify this solution to make the methods accept some generic type T that is or inherits from the various IntX classes? I like this solution because it saves time, but for it to 100% solve the issue (despite not being as nice as if C# had support for this type of constraint, built-in) each of the generated methods should still be generic so that they can return an object of a type that inherits from one of the IntXX classes.
@ZacharyKniebel: the IntXX types are structs which means they don't support inheritance in the first place. And even if it did then the Liskov substitution principle (which you might know from the SOLID idiom) applies: if the method is defined as X and Y is a child of X then per definition any Y should be able to be passed to that method as a substitute of its base type.
This workaround using policies stackoverflow.com/questions/32664/… does use T4 to generate classes.
+1 for this solution since it preserves the operation efficiency of the built-in integral types, unlike the policy based solutions. Calling built-in CLR operators (like Add) through an additional (possibly virtual) method can severely affect performance if used many times (like in mathematical libraries). And since the number of integral types is constant (and can't be inherited from) you only need to regenerate the code for bug fixes.
Very cool and I was just about to start using it then I remembered how dependent on Resharper I am for refactoring and you can't do rename refactor through the T4 template. It's not critical but worth considering.
K
Keith

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.


I wonder if you've seen MiscUtil's support for generic operators... yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html
Yeah - Jon Skeet pointed me at them for something else a while ago (but after this year old response) - they're a clever idea, but I'd still like proper constraint support.
Wait, where T : operators( +, -, /, * ) is legal C#? Sorry for the newbie question.
@kdbanman I don't think so. Keith is saying that C# doesn't support what OP is asking, and is suggesting that we should be able to do where T : operators( +, -, /, * ), but can't.
This is now a thing in .NET 6: devblogs.microsoft.com/dotnet/…
M
Mark Amery

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.


I had issues with this pattern when there are less function arguments than generic parameters. Opened stackoverflow.com/questions/36048248/…
any reason why using struct? what if I use singleton-class instead and change instance to public static NumericPolicies Instance = new NumericPolicies(); and then add this constructor private NumericPolicies() { }.
@M.kazemAkhgary you may use the singleton. I do prefer struct. In theory, it can be optimized by compiler/CLR because the struct contains no information. In case of singleton, you will still pass a reference, which may add additional pressure on GC. Another advantage is that struct can't be null :-) .
I was going to say that you found a very smart solution, but the solution is too limited for me: I was going to use it in T Add<T> (T t1, T t2), but Sum() only works when it can retrieve it's own type of T from it's parameters, which is not possible when it's embedded in another generic function.
V
Vlad

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.


Nice, but not enough... For example, DateTime falls under unmanaged, IComparable, IEquatable<T> constraint..
I know but you can go further depending on your circumstances and add additional interfaces. unmanaged allows to keep this list shorter. i have just shown the approach , approximation using unmanaged. For most cases this is enough
this approach will fail in the important cases where we are implementing math operations T Mult(T a,T b){ return a*b;} can only work work for numeric types not for DateTime.
For most cases this is enough and easy to remember. Improves readability - for those who read code intention is clear.
3
3 revs, 2 users 89%

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

l
ljs

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.


+1, However, // Rest of code... may not compile if it depends on operations defined by the constraints.
Convert.ToIntXX(value) might help make "// Rest of code" compile -- at least until the return type of IntegerFunction is also of type T, then you're hooped. :-p
-1; this doesn't work for the reason given by @Nick. The moment you try to do any arithmetic operations in // Rest of code... like value + value or value * value, you've got a compile error.
H
Haacked

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.


A
Arman Ebrahimpour

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 | ...

I don't see the connection. Unions (whether "discriminated" or not) are about passing around a value that is allowed to contain one of several types. Generic constraints are about calling a method that is known to be supported by a type. The relevant proposal is Updating generic math .. for .NET 7. The difference is that when you call a generic method, it is known what type you have. There is no need for a union. For example, in any class, write public static T MyFunc(T a, T b) { ... }. At compile time T gets resolved to a specific type.
D
Dan

Yes, or rather there soon will be!

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.


R
Rob Deary

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;
        }

Thank you for providing a empirical solution!
Is it not the same than creating the same method for each type?
M
Martin Mulder

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


D
DrGriff

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
}

a
asaf92

.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'm wondered why they still in .NET 5+ didn't mark int16, int32, int64 etc as IInteger? uint16,uint32 etc. as IUInteger just to easily put a constraint in generics where integer type is expected regardless its size. also they could add there some type aka dynamic but for primitives only that will allow to void generics at all in some cases.
M
Martin Marconcini

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.


d
dmihailescu

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 value) where T: IComparable, IFormattable, IConvertible, IComparable, IEquatable, 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.


T
TylerBrinkley

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;
}

d
dbkk

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 work with numerical methods a lot. Sometimes I want integers and sometimes I want floating point. Both have 64 bit versions which are optimal for processing speed. Converting between these is a terrible idea as there are losses each way. While I tend towards using doubles, sometimes I do find it better to use integers because of how they get used elsewhere. But it would be very nice when I am writing an algorithm to do it once and leave the type decision up to the instance requirements.
M
Marc Roussel

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;
}

p
pomeroy

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.


s
supercat

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.


@KenKin: IConvertible provides a means by which any integer could be added to another integer type to yield e.g. an Int64 result, but does not provide a means by which e.g. an integer of arbitrary type could be incremented to yield another integer of the same type.
u
user276648

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.


l
lonix

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);