Problem
Is there a way to limit a generic type argument T to only: Can anyone tell me if there is a way to limit a generic type argument T to only:
I’m aware of the where keyword, but I can’t seem to find an interface that caters just to these sorts.
Something like:
static bool IntegerFunction<T>(T value) where T : INumeric
Asked by Corin Blaikie
Solution #1
This is not possible in C#. In an interview with Bruce Eckel, Hejlsberg explained why the feature was not implemented:
For each T that they intend to utilize, they must implement alculatorT>. You can get away with a reasonably simple interface if it doesn’t have to be extensible, i.e. if you only want to support a set number of types, such as int and double:
var mat = new Matrix<int>(w, h);
(Implementation is in the form of a GitHub Gist.)
You must, however, open up this implementation so that the user can offer their own Calculator instances if you want the user to be able to supply their own custom types. To create a matrix that employs a proprietary decimal floating point implementation, DFP, for example, you’d need to write the following code:
var mat = new Matrix<DFP>(DfpCalculator.Instance, w, h);
… and ICalculatorDFP> implements all of DfpCalculator’s members.
Working with policy classes, as explained in Sergey Shandar’s answer, is a solution that, regrettably, has the same constraints.
Answered by Konrad Rudolph
Solution #2
Given the prevalence of this query and the interest in such a function, I’m amazed there isn’t yet a T4-related answer.
I’ll show you a simple example of how you can utilize the sophisticated templating engine to achieve what the compiler does behind the scenes with generics in this sample code.
Rather to jumping through hoops and compromising compile-time certainty, you may simply construct the function you want for each type and use it as needed (at compile time!).
To do so, you’ll need to:
<#@ 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 is all there is to it. You’ve completed the task.
When you save this file, it will be immediately compiled into the following 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;
}
}
You can check that you have compile-time certainty in your main method:
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 jump forward to one point: this does not violate the DRY concept. The DRY concept exists to prevent users from repeating code in several places, which would make it difficult to maintain the application.
This is not the case here: if you want to alter something, all you have to do is edit the template (one source for all your generation!) and you’re done.
Add a namespace declaration to your produced code (make sure it’s the same as the one where you’ll specify your own implementation) and designate the class as partial to utilize it with your own custom definitions. After that, add the following lines to your template file to ensure that it is included in the final compilation:
<#@ import namespace="TheNameSpaceYouWillUse" #>
<#@ assembly name="$(TargetPath)" #>
Let’s face it, this is really awesome.
Disclaimer: This sample was greatly influenced by Kevin Hazzard and Jason Bock’s Manning Publications book Metaprogramming in.NET.
Answered by Jeroen Vannevel
Solution #3
This isn’t restricted in any way. It’s a serious problem for anyone who wants to do quantitative calculations with generics.
I’d even go so far as to suggest that we require
static bool GenericFunction<T>(T value)
where T : operators( +, -, /, * )
Or even
static bool GenericFunction<T>(T value)
where T : Add, Subtract
Only interfaces, base classes, and the terms struct (must be a value type), class (must be a reference type), and new() are available (must have default constructor)
Like here on codeproject, you may wrap the number in something else (similar to INullableT>).
You might apply the constraint at runtime (via reflecting for operators or checking for types), but that defeats the purpose of having the generic to begin with.
Answered by Keith
Solution #4
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 safe to use at compile time. For.NET 4.0, the CityLizard Framework provides a compiled version. lib/NETFramework4.0/CityLizard.Policy.dll is the location of the file.
CityLizard is also available as a Nuget package: https://www.nuget.org/packages/CityLizard/. See CityLizard.Policy for further information. I’m a planner.
Answered by Sergey Shandar
Solution #5
Closer approximation – the unmanaged constraint can be used to define that a type parameter is a non-pointer, non-nullable unmanaged type starting with C# 7.3.
class SomeGeneric<T> where T : unmanaged
{
//...
}
The unmanaged constraint is equivalent to the struct constraint and cannot be combined with the struct or new() constraints.
If a type is any of the following, it is an unmanaged type:
Add IComparable (although enum is still derived from IComparable, therefore restrict enum by adding IEquatable T >) to further restrict and exclude pointer and user-defined types that do not implement IComparable. Depending on your circumstances, you can go even farther and add more interfaces. This list can be kept shorter by using the unmanaged option):
class SomeGeneric<T> where T : unmanaged, IComparable, IEquatable<T>
{
//...
}
However, this does not prohibit DateTime from being instantiated.
Answered by Vlad Novakovsky
Post is based on https://stackoverflow.com/questions/32664/is-there-a-constraint-that-restricts-my-generic-method-to-numeric-types