Generics - Where T Is a Number

Generics - where T is a number?

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

Re the comment about < / > - you don't actually need operators for this; you just need:

T x = ..., T y = ...
int c = Comparer<T>.Default.Compare(x,y);
if(c < 0) {
// x < y
} else if (c > 0) {
// x > y
}

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

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.

Generic Type Constraint to accept only Numbers?

At the great cost of losing readability, you can rewrite all methods from

int Factorial(int x)
{
if (x == 0) return 1;
return x * Factorial(x - 1);
}

to

T Factorial<T>(IReal<T> t, T x)
{
if (t.Equals(x, t.FromInteger(0))) return t.FromInteger(1);
return t.Multiply(x, Factorial<T>(t, t.Subtract(x, t.FromInteger(1))));
}

using

interface IEq<T>
{
bool Equals(T a, T b);
}

interface IOrd<T> : IEq<T>
{
int Compare(T a, T b);
}

interface IShow<T>
{
string Show(T a);
}

interface INum<T> : IEq<T>, IShow<T>
{
T Add(T a, T b);
T Subtract(T a, T b);
T Multiply(T a, T b);

T Negate(T a);

T Abs(T a);
T Signum(T a);

T FromInteger(int x);
}

interface IReal<T> : INum<T>, IOrd<T>
{
}

with implementations for any number type.

How can I specify that Typescript generic T[K] is number type?

I won't know what structure of someObj will be.

Then TypeScript can't really help you with this. TypeScript's type checking is done at compile-time. If the structure of someObj will only be known at runtime, TypeScript can't make access to that structure typesafe. You'd need to know what the property keys and possible values for those properties are at compile-time.

For instance: In your example, the property names are strings and the property values are either strings or numbers (but not booleans or objects, etc.). You can declare a type indexed by strings (since all property names are ultimately strings or Symbols, in this case strings) where the property values are numbers or strings:

declare type SomeObjType = {
[key: string]: number | string
};

and then getPropertyis:

function getProperty<T extends SomeObjType>(obj: T, key: string): number | string {
return obj[key];
}

and you can use it like this (in this case, I use JSON.parse to simulate receiving this data from outside the scope of the program):

const someObj: SomeObjType = JSON.parse(`{
"myValue": 123,
"otherProperty": "321"
}`);

console.log(getProperty(someObj, 'myValue'));
console.log(getProperty(someObj, 'otherProperty'));

On the playground

But that doesn't buy you much, and closes off the possibility that the property values are something other than numbers or strings.

You may need to just use object:

function getProperty(obj: object, key: string) {
return obj[key];
}

const someObj = JSON.parse(`{
"myValue": 123,
"otherProperty": "321"
}`);

console.log(getProperty(someObj, 'myValue'));
console.log(getProperty(someObj, 'otherProperty'));

On the playground

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.

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.

When using generics, how can I determine if T is negative?

Yes, it is possible to do this in one line with no type checking. There is one caveat though - this method will not play nice if the concrete type doesn't implement IComparable or IComparable<T>. If you don't implement one of those interfaces then you will get a System.ArgumentException, but this is easily accommodated (especially as it is most likely to occur for one of your own custom types, I don't think there are many BCL types that don't implement it).

All you need is this nifty line of code:

bool isNegative = Comparer<T>.Default.Compare(value, default(T)) < 0;

The real beauty in this one is the default(T) - this is what accommodates strings, DateTimes, etc. The concrete type will get compared to the default value of the type, and for the likes of string or DateTime the Compare() will never return less than zero when comparing to the default.

Check out this sample, which includes an example custom struct so you can see the IComparable in action:

class Program
{

static void Main(string[] args)
{

Console.WriteLine("Comparing different value (and reference) types to zero:");
Console.WriteLine();
Console.WriteLine("Checking int '9' : {0}", IsNegative(9));
Console.WriteLine("Checking int '-9': {0}", IsNegative(-9));
Console.WriteLine("Checking int '0' : {0}", IsNegative(0));
Console.WriteLine();
Console.WriteLine("Checking decimal '9' : {0}", IsNegative(9m));
Console.WriteLine("Checking decimal '-9': {0}", IsNegative(-9m));
Console.WriteLine("Checking decimal '0' : {0}", IsNegative(0m));
Console.WriteLine();
Console.WriteLine("Checking float '9' : {0}", IsNegative(9f));
Console.WriteLine("Checking float '-9' : {0}", IsNegative(-9f));
Console.WriteLine("Checking float '0' : {0}", IsNegative(0f));
Console.WriteLine();
Console.WriteLine("Checking long '9' : {0}", IsNegative(9L));
Console.WriteLine("Checking long '-9' : {0}", IsNegative(-9L));
Console.WriteLine("Checking long '0' : {0}", IsNegative(0L));
Console.WriteLine();
Console.WriteLine("Checking string '9' : {0}", IsNegative("9"));
Console.WriteLine("Checking string '-9' : {0}", IsNegative("-9"));
Console.WriteLine("Checking string empty : {0}", IsNegative(string.Empty));
Console.WriteLine("Checking string null : {0}", IsNegative((string)null));
Console.WriteLine();
Console.WriteLine("Checking DateTime.Now '{0}' : {1}", DateTime.Now, IsNegative(DateTime.Now));
Console.WriteLine("Checking DateTime.Max '{0}' : {1}", DateTime.MaxValue, IsNegative(DateTime.MaxValue));
Console.WriteLine("Checking DateTime.Min '{0}' : {1}", DateTime.MinValue, IsNegative(DateTime.MinValue));
Console.WriteLine();
Console.WriteLine("Checking positive MyStruct : {0}", IsNegative(new MyStruct() { MainValue = 9 }));
Console.WriteLine("Checking negative MyStruct : {0}", IsNegative(new MyStruct() { MainValue = -9 }));
Console.WriteLine("Checking zero MyStruct : {0}", IsNegative(new MyStruct() { MainValue = 0 }));
Console.WriteLine();

Console.ReadKey();
}

private static bool IsNegative<T>(T value)
{
return Comparer<T>.Default.Compare(value, default(T)) < 0;
}

private struct MyStruct : IComparable<MyStruct>, IComparer<MyStruct>
{
public int MainValue;

public int CompareTo(MyStruct other)
{
return MainValue - other.MainValue;
}

public int Compare(MyStruct x, MyStruct y)
{
return x.MainValue - y.MainValue;
}
}
}

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

Java Generic Type Number

Maybe this is your intention:

public static <N extends Number> double multiply(N x, N y){
return x.doubleValue() * y.doubleValue();
}

Although I must also say that the generic use of Number instead of concrete immutable value types like java primitive double is probably not so healthy because in the example above the arguments could even be of different types, for example Integer and Double.

Attention:

I confirm, the arguments can be of different types as given signature above. So the answer of Bohemian is wrong. I have tested it just now (but knew it already before). The compiler only guarantees that both arguments are of type Number, nothing else.

In order to assert the same argument types the compiler needs self-referencing generics. This feature is not fulfilled by Number-class (that is <N extends Number<N>> is unfortunately not possible). That is why I consider the whole Number approach as not really healthy. Here a test code which everyone can execute:

Integer x = Integer.valueOf(10);
Double y = new Double(2.5);
System.out.println(multiply(x, y));
// Output: 25.0


Related Topics



Leave a reply



Submit