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
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 singleInt16
,Int32
, etc. and implement those interfaces, and then have methods which accept generic types constrained toISignedWholeNumber
, 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 propertybool Available {get;};
and static delegates forInt64 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 typeT
implements methods with the proper names and signatures (in case it's something like a struct which contains anInt64
and represents a number, but has a customToString()
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.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 {...
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:-
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.
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:
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:
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.
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.
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:
That's it. You're done now.
Saving this file will automatically compile it to this source file:
In your
main
method you can verify that you have compile-time certainty: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: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.