Is There a Constraint That Restricts My Generic Method to Numeric Types

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

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.

Generic constraint to match numeric types

In this case you want to constrain your generic to the IComparable interface, which gives you access to the CompareTo method, since this interface allows you to answer the question ShouldBeGreaterThan.

Numeric types will implement that interface and the fact that it also works on strings shouldn't bother you that much.

Generics that restricts the types to Int, Double, long

This is not possible currently.

Also, see this question for a possible workaround

C# generic method with integer constraint refuses to cast from integer to the generic type

"Only class or interface could be specified as constraint." (c) ReSharper

int (Int32) is just a struct. You can constrain that T is a struct. but you can't use any struct as constraint.

the whole list of possible constraints you can find here - http://msdn.microsoft.com/en-us/library/d5x73970.aspx

UPD

and for Enum constraint see this question - Is there a workaround for generic type constraint of "special class" Enum in C# 3.0?

Restrict generic T to specific types

You can't restrict generics in that way, you can only choose a single class as a constraint. You must either make 5 overloads or find a interface all 5 of those things share and use that. Which option you choose will depend on what Validate doe.

Here is how you would do the overloads.

public static IResult<Int16, String> Validate<T>(this Int16 value) {
} // Validate

public static IResult<Int32, String> Validate<T>(this Int32 value) {
} // Validate

public static IResult<Int64, String> Validate<T>(this Int64 value) {
} // Validate

public static IResult<double, String> Validate<T>(this double value) {
} // Validate

public static IResult<String, String> Validate<T>(this String value) {
} // Validate

Here is by using a common interface, all of the members you list Implement IConvertible so you could restrict by that, however this will allow any IConvertible not just the 5 you listed.

public static IResult<T, String> Validate<T>(this T value) where IConvertible {
} // Validate

Limit a generic type argument only to be a numeric type

No, there is still no generic type constraint for a "numeric" type.

To get your T a = 0 specifically you can just use T a = default(T). The default of all numeric types is 0.

How to restrict T to value types using a constraint?

Unfortunately, it is not possible to specify generic type constraints that only allow specific value types. More to the point, it wouldn't make much sense even if it was allowed.

You're allowed to specify a class as a generic constraint but this is because you can inherit from classes, the constraint thus sets the minimum threshold of what types you're allowed to use.

If this was allowed for value types, where you cannot inherit from those types, you would effectively limit yourself to only that type.

Thus you cannot do this, but you have a few alternatives:

  • You can declare it without the constraint, and handle the problem at runtime. I would not recommend this way
  • You can declare overloads that take the specific types you're interested in.

    Since you only have two such types this is what I would recommend doing.

Here are the overloads you would declare:

public static Chart PopulateInto<T>(List<T> yAxis, List<int> xAxis)
{
// Do stuff here
}

public static Chart PopulateInto<T>(List<T> yAxis, List<decimal> xAxis)
{
// Do stuff here
}

Now, additionally, if your handling of those values doesn't really rely on the numeric quality of those types, you just want to limit which types you can handle, then you can always declare your original method as well, privately, and call this method from your overloads. This would still limit your code to only allowing int or decimal, publicly, but your implementation would still be generic. Without knowing exactly what "Do stuff here" entails it is impossible to tell if this is a viable option or not but here is the code anyway:

public static Chart PopulateInto<T>(List<T> yAxis, List<int> xAxis)
{
return PopulateInto<T, int>(yAxis, xAxis);
}

public static Chart PopulateInto<T>(List<T> yAxis, List<decimal> xAxis)
{
return PopulateInto<T, decimal>(yAxis, xAxis);
}

private static Chart PopulateInto<T, N>(List<T> yAxis, List<N> xAxis) where N : struct
{
// Do stuff here
}

Generic type constraint for numerical type only

You can specify type constraints (using both classes and protocols) for a generic class (same syntax applies to functions) using angle brackets:

class Foo<T: Equatable, U: Comparable> { }

To specify more than one requirement on a single type, use a where clause:

class Foo<T: UIViewController where T: UITableViewDataSource, T: UITextFieldDelegate> { }

However, it doesn't look like you can specify optional requirements in a generic parameter clause, so one possible solution is to create a protocol that all the numeric types implement via extensions and then constrain your class on that requirement:

protocol Numeric { }

extension Float: Numeric {}
extension Double: Numeric {}
extension Int: Numeric {}


class NumberCruncher<C1: Numeric> {
func echo(num: C1)-> C1 {
return num
}
}

NumberCruncher<Int>().echo(42)
NumberCruncher<Float>().echo(3.14)

Is there a C# generic constraint for real number types?

You can't define such a constraint, but you could check the type at runtime. That won't help you for doing calculations though.

If you want to do calculations, something like this would be an option:

class Calculations<T, S> where S: Calculator<T>, new()
{
Calculator<T> _calculator = new S();

public T Square(T a)
{
return _calculator.Multiply(a, a);
}

}

abstract class Calculator<T>
{
public abstract T Multiply(T a, T b);
}

class IntCalculator : Calculator<int>
{
public override int Multiply(int a, int b)
{
return a * b;
}
}

Likewise, define a FloatCalculator and any operations you need. It's not particularly fast, though faster than the C# 4.0 dynamic construct.

var calc = new Calculations<int, IntCalculator>();
var result = calc.Square(10);

A side-effect is that you will only be able to instantiate Calculator if the type you pass to it has a matching Calculator<T> implementation, so you don't have to do runtime type checking.

This is basically what Hejlsberg was referring to in this interview where the issue is discussed. Personally I would still like to see some kind of base type :)



Related Topics



Leave a reply



Submit