Repository navigation
Add a special Numeric structure or INumeric Interface #25058
Description
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, 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 dotnet/csharplang#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.
Activity
jnm2 commentedon Feb 16, 2018
I'm pretty sure dotnet/csharplang#164 is going to be the answer for this.
[-]Add a special Numeric structure [/-][+]Add a special Numeric structure or INumeric Interface[/+]yaakov-h commentedon Feb 16, 2018
Wouldn't that be doable with just dotnet/csharplang#104?
jnm2 commentedon Feb 16, 2018
@MohammadHamdyGhanem
@yaakov-h's suggestion is better, but off the top of my head you'd make the method take
<T>whereT : SEnumand define the|operator in anSEnumshape.jnm2 commentedon Feb 16, 2018
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
ILiteralorIPrimitiveif you had one?jnm2 commentedon Feb 16, 2018
But what can you do with
where T : IPrimitivethat you can't do without that constraint?jnm2 commentedon Feb 16, 2018
"Just in case" proposals are often considered an antipattern.
aaronfranke commentedon Feb 28, 2018
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, ordecimal. With only<T>andT x, y, z;I am unable to do things likez = x + y;orif (x > y)dotnet/csharplang#1345
eerhardt commentedon Mar 15, 2018
The original proposals of having a
struct Numericthat wraps anobject, or having anINumericinterface, 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
1 remaining item
aaronfranke commentedon Mar 16, 2018
@MohammadHamdyGhanem I think it would make more sense to call it
numberinstead ofarithkhellang commentedon Mar 16, 2018
I think this will be solved by dotnet/csharplang#110.
See @MattWindsor91's writeup at https://github.com/MattWindsor91/roslyn/blob/master/concepts/docs/csconcepts.md
ericstj commentedon Mar 16, 2018
@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 experiments but my understanding is that an interface and instance methods had both poor performance and bad semantics (because they cannot use operators).
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.
tannergooding commentedon Apr 22, 2021
Closing this. As mentioned in other issues (such as #50647) we are looking into the correct surface area to expose here as part of the
static abstracts in interfacesfeature the language and runtime are adding.This is currently being tracked in our dotnet/designs repo and I've added the initial rough draft of the surface area here: dotnet/designs#205
Further discussion or suggestions should happen there and in future PRs that further refine the proposed surface area.