_Intrinsic_ numeric types (byte, int, float, ….etc) are structures, so unfortunately, they have no base class that can gather all basic methods and operators!
So I suggest to add a special Numeric structure that can accept numeric values of any type. It should contain the value and the underling type of it, as Object class does. Numeric structure should have the basic arithmetic and logical operators. Or it can be an interace (say INumeric) that all numeric data types implement. This will not break any existing code.
Usage:
Numeric structure (or INumeric Inrerface) can be used in generic methods where T is expected to be a numeric type, to carry out the basic arithmetic and logical operators without writing a dozen if statements for each individual numeric type. Objects doesn't have the basic arithmetic and logical operators and have to be converted to numeric types to work with. For example, I tried to do this but failed:
```
public struct Numeric
{
public object Value { get; set; }
public static Numeric operator +(Numeric n1, Numeric n2)
{
return (Numeric)(n1.Value + n2.Value);
}
}
C# refuses n1.Value + n2.Value because it doesn't know how to add objects. Many if statements are needed to deal with different types of n1 and n2, regarding the larger type to use as the output type. This is a hell of code to something that should be easy and direct if there was a general numeric type that can contain any numeric type.
I face the same situation when I use the base Enum class in any method, because Enum can have any numeric type as an underling type!
I saw similar problem in the source code of Vector<T>, where tons of code lines are repeated just to cover all cases of each numeric type!
Note: If you add the numeric type, C# and VB.net should add a new constraint for generic parameters to be numerics.
where T: numeric
this means T can only be of type Numeric or any intrinsic numeric data types.
So, If this Numeric Type or INumeric Interface exists, one can write
public static Enum SetFlag(Enum value, Enum flag)
{
var result = (Numeric)value | (Numeric)flag);
return (Enum)result;
}
instead of:
public static Enum SetFlags(Enum value, Enum flag)
{
if (Enum.GetUnderlyingType(value.GetType()) == typeof(byte))
return (Enum)(object)((byte)(object)value | (byte)(object)flag);
if (Enum.GetUnderlyingType(value.GetType()) == typeof(int))
return (Enum)(object)((int)(object)value | (int)(object)flag);
//
//
// etc
}
```
and the same when writing generic types that deal with numeric data types only.
This can be extended based on this proposal https://github.com/dotnet/csharplang/issues/1233, I also suggest to add these interfaces:
numeric constraint.
IIntegral: for integer tyoes (byte, sbyte, int, uint, short, ushort, long, ulong)
IFloating: (float, double, decemal)
IValue: (numerics and dates).
ILetiral (Char and string).
IPrimitive (numerics, dates, char, string)
These Interfaces will make writting generics easier. IPerimative can help you
format or parsing or using + operation depending on where T: IPerimative.
Also it can be usefull when defining generic lists and dictionaries that can contain different values. This will make them more specialized than using objects.
I'm pretty sure https://github.com/dotnet/csharplang/issues/164 is going to be the answer for this.
Could you explain how I do some thing like this with shapes?
public static Enum SetFlag(Enum value, Enum flag)
{
var result = (INumeric)value | (INumeric)flag);
return (Enum)result;
}
Wouldn't that be doable with just https://github.com/dotnet/csharplang/issues/104?
public static TEnum SetFlag<TEnum>(TEnum value, TEnum flag) where TEnum : Enum, struct
=> value | flag;
I asked for similar lang. feature here
https://github.com/dotnet/csharplang/issues/1233
But i think making it in the framework will be a general solution for all languages.
Based on my c# proposal, I also suggest to add these interfaces:
numeric constraint.
IIntegral: for integer tyoes (byte, sbyte, int, uint, short, ushort, long, ulong)
IFloating: (float, double, decemal)
IValue: (numerics and dates).
|Letiral (Char and string).
IPrimitive (numerics, dates, char, string)
@MohammadHamdyGhanem
Could you explain how I do some thing like this with shapes?
@yaakov-h's suggestion is better, but off the top of my head you'd make the method take <T> where T : SEnum and define the | operator in an SEnum shape.
IIntegral, IFloating, ILiteral, IPrimitive
As soon as you use any of these things as parameter or return types, you end up boxing all these tiny value types on the heap. Not good for performance.
Also, DateTime and decimal are not primitives as far as the type system is concerned.
All of that begs the question, what would you do with an instance of ILiteral or IPrimitive if you had one?
These Interfaces will make writting generics easier. IPerimative can help you
format or parsing or using + operation depending on where T: IPerimative.
Also it can be usefull when defining generic lists and dictionaries that can contain different values. This will make them more specialized than using objects.
But what can you do with where T : IPrimitive that you can't do without that constraint?
In fact, I faced situations that need INumeric, and this is the most important. I proposed the other as a general solution "just in case".
The other constraints are in desdendent order regarding their importance.
I proposed the other as a general solution "just in case".
"Just in case" proposals are often considered an antipattern.
I am interested in numeric constraints so that I can create math classes that can use any numeric data type. For example, to make a Vector3 that can use float, double, or decimal. With only <T> and T x, y, z; I am unable to do things like z = x + y; or if (x > y)
The original proposals of having a struct Numeric that wraps an object, or having an INumeric interface, are not feasible. This would cause boxing of every number, which will not perform satisfactorily.
However, the issue described here is an issue that we would like to fix some point. We have the same need in the Tensor<T> type in corefxlab: https://github.com/dotnet/corefxlab/blob/master/src/System.Numerics.Tensors/System/Numerics/TensorArithmetic.tt.
We would probably need a combination of new C# language features, and a built-in "Arthimetic" shape/type-class.
/cc @ericstj
@eerhardt
So, all we need is a new keyword that works as arithmetic constrain (i.e. enum, arith or num)
class foo<T> where T : arith
{
void foo1(T x, T y)
{
T z = x + y;
}
}
The purpose of the arith keyword is to tell the compiler at design time that x + y is a valid operation, and to ensure that T must be a numeric data type. At run time, T is known and x + y should execute as normal.
It came to me now, that we also need to constrain object and dynamic types. maybe something like this:
object x = enum1 where x: arith ;
object y = enum2 where y: arith;
var z = x + y;
Or it can be shortened like this:
var z = (arith)enum1 + (arith)enum2;
But I think Enum class should be some how marked as arith, so there will be no need for any more casting:
var z = enum1 + enum2;
@MohammadHamdyGhanem I think it would make more sense to call it number instead of arith
@aaronfranke
Any suitable name is OK.
I think this will be solved by https://github.com/dotnet/csharplang/issues/110.
See @MattWindsor91's writeup at https://github.com/MattWindsor91/roslyn/blob/master/concepts/docs/csconcepts.md
I looked at the Int32 structure here:
https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/int32.cs
There is a commented out code that implements the IArithmetic
https://stackoverflow.com/questions/41832780/iarithmetict-interface-in-c-sharp
Clearly there was trilas to solve the numeric types problem, but it is not successfull yet!
@jnm2 , @yaakov-h , @aaronfranke , @eerhardt , @khellang
I posted a new proposal with a new approach:
dotnet/csharplang#1387, Proposal: define signatures for generic type parameters
@MohammadHamdyGhanem indeed, folks care about this problem very much. It's no coincidence that multiple language proposals show how they intend to help solve this problem. I wasn't around for the initial IArithmetic
For operators you need static interfaces, static interfaces could also solve the performance problem since the JIT could assume an impl based on the T and specialize to optimize away the method call without actually having to box the primitive type. This was alluded to in @MattWindsor91's writeup: https://github.com/MattWindsor91/roslyn/blob/master/concepts/docs/csconcepts.md#static-interface-methods-for-the-clr
As mentioned static interfaces require changes in both runtime and frameworks so its less than ideal for reach.
I think the current C# proposal for type classes/shapes has much more promise. It has the potential to work with purely compiler features, though we might add some default implementations of the shapes/concepts/whatever-they-get-named in the framework itself to facilitate ease of use and type exchange.
I'd highly recommend you look through the type class / shapes issues in CSharpLang. I believe those are good generalizations of this type of problem and have the best chance at getting a solution that can reach to the largest number of C# developers.
@ericstj
Thanks for the details.
I proposed to define a segnature dotnet/csharplang#1387dotnet/csharplang#1387, Proposal: define signatures for generic type parameters. Maybe it is like the contract, but my aim is for generics, and no need to change the current CoreFX classes and structs to deal with.
@yaakov-h
I tried the Enum constraint but it refuses to use int as a type param, saying int can't be converted to Enum. So, unfortunately, Enum is not for basic numeric types!
Most helpful comment
I'm pretty sure https://github.com/dotnet/csharplang/issues/164 is going to be the answer for this.