How to check if the type is numeric?

public static bool IsNumeric(this Type type) { ... } 

The solution is interesting for both built-in types and third-party / private ones.

  • And what is a numeric type , taking into account the generalization to non-embedded? Have any definition / requirements ? - Dmitry D.
  • Numeric types are types that store numbers. When you perform math operations, you are dealing with numeric values. source of the quote is 4per
  • Quote so-so. Let's drop the primitive types. Arithmetic operations can be defined for a wide variety of types. For example, for Nullable<T> : (int?)1 + (int?)2 . As well as for complex types: complex numbers, vectors, etc .. A number can be stored in a single field as an array of bytes. Will this type be numeric ? Perhaps, examples of the practical use of the IsNumeric method IsNumeric shed light on the meaning of this mysterious term ...;) - Dmitry D.
  • or maybe we’ll better discard sophistry))? go to the link from the answer - for some reason nobody asked what Numeric Type is .. - 4per
  • This is not sophistry. If you want to get a good answer to your question (in your case - to get rid of the disadvantages of the approach in the answer), you need to formulate the question so that it becomes possible. This link was not about user types, only primitive ones . Otherwise, I am convinced, the question of definition would not take long to wait. So how do you use the IsNumeric method? Perhaps, you are only interested in whether the type of arithmetic operators are defined? - Dmitry D.

2 answers 2

Decision based on https://stackoverflow.com/questions/1749966/c-sharp-how-to-determine-whether-a-type-is-a-number

 public static bool IsNumeric(this Type type) { HashSet<Type> NumericTypes = new HashSet<Type> { //встроенные: typeof(Byte), typeof(SByte), typeof(UInt16), typeof(UInt32), typeof(UInt64), typeof(Int16), typeof(Int32), typeof(Int64), typeof(Decimal), typeof(Double), typeof(Single), //сторонние: typeof(Oracle.DataAccess.Types.OracleDecimal) }; return NumericTypes.Contains(type); } 

Disadvantages:

  • you need to add each new type to the code, probably during the treatment of the next bug
  • one
    Remove the definition of NumericTypes from the method. - Trymount

Definitions

Since the formulation of the problem includes custom types, we define a numeric type as follows:

Types that implement arithmetic operators + , - (binary), - (unary), / , * , are called numeric .

At the same time, we define integer types like this:

Types that implement bit operators & , | , ^ , ~ , are called integers .

If necessary, there are no obstacles to expanding the list of operators in any of these definitions.


Implementation

The following class, based on the MiscUtils library and using Linq.Expressions can help to solve this problem:

 using System; using System.Linq.Expressions; using System.Runtime.Serialization; /// <summary> /// Provides easy access to the standard operators (addition, etc) /// for generic types, using type inference to simplify usage. /// </summary> public static class Numeric<T> { /// <summary> /// Indicates that the <typeparamref name="T"/> type has the arithmetic operators. /// </summary> public static readonly bool IsNumeric; /// <summary> /// Indicates that the <typeparamref name="T"/> type has the bitwise arithmetic operators. /// </summary> public static readonly bool IsInteger; /// <summary> /// Create a function delegate representing a unary operation. /// </summary> /// <typeparam name="TArg1">The parameter type.</typeparam> /// <typeparam name="TResult">The return type.</typeparam> /// <param name="body">Body factory.</param> /// <param name="indicator">The <c>false</c> is set in this <c>bool</c> variable if there is no such operator.</param> /// <returns>Compiled function delegate.</returns> private static Func<TArg1, TResult> CreateExpression<TArg1, TResult>( Func<Expression, UnaryExpression> body, ref bool indicator) { ParameterExpression inp = Expression.Parameter(typeof(TArg1), "inp"); try { return Expression.Lambda<Func<TArg1, TResult>>(body(inp), inp).Compile(); } catch (Exception ex) { indicator = false; string msg = ex.Message; // avoid capture of ex itself return delegate { throw new InvalidOperationException(msg); }; } } /// <summary> /// Create a function delegate representing a binary operation. /// </summary> /// <typeparam name="TArg1">The first parameter type.</typeparam> /// <typeparam name="TArg2">The second parameter type.</typeparam> /// <typeparam name="TResult">The return type.</typeparam> /// <param name="body">Body factory.</param> /// <param name="indicator">The <c>false</c> is set in this <c>bool</c> variable if there is no such operator.</param> /// <returns>Compiled function delegate.</returns> private static Func<TArg1, TArg2, TResult> CreateExpression<TArg1, TArg2, TResult>( Func<Expression, Expression, BinaryExpression> body, ref bool indicator) { return CreateExpression<TArg1, TArg2, TResult>(body, false, ref indicator); } /// <summary> /// Create a function delegate representing a binary operation. /// </summary> /// <param name="castArgsToResultOnFailure"> /// If no matching operation is possible, attempt to convert /// TArg1 and TArg2 to TResult for a match? For example, there is no /// "decimal operator /(decimal, int)", but by converting TArg2 (int) to /// TResult (decimal) a match is found. /// </param> /// <typeparam name="TArg1">The first parameter type.</typeparam> /// <typeparam name="TArg2">The second parameter type.</typeparam> /// <typeparam name="TResult">The return type.</typeparam> /// <param name="body">Body factory.</param> /// <param name="indicator">The <c>false</c> is set in this <c>bool</c> variable if there is no such operator.</param> /// <returns>Compiled function delegate.</returns> private static Func<TArg1, TArg2, TResult> CreateExpression<TArg1, TArg2, TResult>( Func<Expression, Expression, BinaryExpression> body, bool castArgsToResultOnFailure, ref bool indicator) { ParameterExpression lhs = Expression.Parameter(typeof(TArg1), "lhs"); ParameterExpression rhs = Expression.Parameter(typeof(TArg2), "rhs"); try { try { return Expression.Lambda<Func<TArg1, TArg2, TResult>>(body(lhs, rhs), lhs, rhs).Compile(); } catch (InvalidOperationException) { if (castArgsToResultOnFailure && !( // if we show retry typeof(TArg1) == typeof(TResult) && // and the args aren't typeof(TArg2) == typeof(TResult))) { // already "TValue, TValue, TValue"... // convert both lhs and rhs to TResult (as appropriate) Expression castLhs = typeof(TArg1) == typeof(TResult) ? (Expression)lhs : Expression.Convert(lhs, typeof(TResult)); Expression castRhs = typeof(TArg2) == typeof(TResult) ? (Expression)rhs : Expression.Convert(rhs, typeof(TResult)); return Expression.Lambda<Func<TArg1, TArg2, TResult>>( body(castLhs, castRhs), lhs, rhs).Compile(); } throw; } } catch (Exception ex) { indicator = false; string msg = ex.Message; // avoid capture of ex itself return delegate { throw new InvalidOperationException(msg); }; } } public static readonly T Zero; public static readonly Func<T, T, T> Add; public static readonly Func<T, T, T> Sub; public static readonly Func<T, T, T> Mul; public static readonly Func<T, T, T> Div; public static readonly Func<T, T> Neg; public static readonly Func<T, T, T> Or; public static readonly Func<T, T, T> And; public static readonly Func<T, T, T> Xor; public static readonly Func<T, T> Not; static Numeric() { IsNumeric = true; Add = CreateExpression<T, T, T>(Expression.Add, ref IsNumeric); Sub = CreateExpression<T, T, T>(Expression.Subtract, ref IsNumeric); Mul = CreateExpression<T, T, T>(Expression.Multiply, ref IsNumeric); Div = CreateExpression<T, T, T>(Expression.Divide, ref IsNumeric); Neg = CreateExpression<T, T>(Expression.Negate, ref IsNumeric); IsInteger = true; And = CreateExpression<T, T, T>(Expression.And, ref IsInteger); Or = CreateExpression<T, T, T>(Expression.Or, ref IsInteger); Not = CreateExpression<T, T>(Expression.Not, ref IsInteger); Xor = CreateExpression<T, T, T>(Expression.ExclusiveOr, ref IsInteger); Type typeT = typeof(T); if (typeT.IsValueType && typeT.IsGenericType && typeT.GetGenericTypeDefinition() == typeof(Nullable<>)) { // get the *inner* zero (not a null Nullable<TValue>, but default(TValue)) Type nullType = typeT.GetGenericArguments()[0]; Zero = (T)Activator.CreateInstance(nullType); } else if (typeT.IsValueType) { Zero = default(T); } else { Zero = typeT == typeof(string) ? (T)(object)String.Empty : (T)FormatterServices.GetUninitializedObject(typeT); } } /// <summary> /// Calculate the squared specified value. /// </summary> /// <param name="x">The value.</param> /// <returns>The squared value.</returns> public static T Pow2(T x) { return Mul(x, x); } /// <summary> /// Raise the specified value to the 3rd power. /// </summary> /// <param name="x">The value.</param> /// <returns>Value raised to the 3rd power.</returns> public static T Pow3(T x) { return Mul(Mul(x, x), x); } /// <summary> /// Raise the specified value to the Nth power. /// </summary> /// <param name="x">The value.</param> /// <param name="n">The power.</param> /// <returns>Value raised to the Nth power.</returns> public static T Pow(T x, int n) { if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n)); T prod = x; for (int i = 1; i < n; i++) prod = Mul(prod, x); return prod; } } 

Example of use:

 Console.WriteLine(Numeric<int>.IsNumeric); // true Console.WriteLine(Numeric<int>.IsInteger); // true Console.WriteLine(Numeric<float>.IsNumeric); // true Console.WriteLine(Numeric<float>.IsInteger); // false Console.WriteLine(Numeric<string>.IsNumeric); // false Console.WriteLine(Numeric<string>.IsInteger); // false 

Example for custom type:

 internal sealed class A { public readonly int Value; public A(int value) { Value = value; } public static A operator +(A a, A b) { return new A(a.Value + b.Value); } public static A operator -(A a, A b) { return new A(a.Value - b.Value); } public static A operator *(A a, A b) { return new A(a.Value * b.Value); } public static A operator /(A a, A b) { return new A(a.Value / b.Value); } public static A operator -(A a) { return new A(-a.Value); } public override string ToString() { return Value.ToString(); } } 

Issuance:

 Console.WriteLine(Numeric<A>.IsNumeric); // true Console.WriteLine(Numeric<A>.IsInteger); // false 

By-effect

In general, this class was conceived as a means of using arithmetic for generic types. In the context of the current task, this is a pleasant side effect. For example, take this class:

 internal sealed class MyVector<T> { private readonly List<T> values; public readonly ReadOnlyCollection<T> Values; public MyVector(params T[] collection) { if (!Numeric<T>.IsNumeric) throw new NotSupportedException($"The type {typeof(T).FullName} must be numeric"); values = new List<T>(collection); Values = new ReadOnlyCollection<T>(values); } public void Add(T item) { values.Add(item); } public T Sum => values.Aggregate(Numeric<T>.Zero, Numeric<T>.Add); public T SumPow2 => values.Select(Numeric<T>.Pow2).Aggregate(Numeric<T>.Zero, Numeric<T>.Add); } 

Pay attention to the properties of Sum and SumPow2 . If type T is numeric , then in these properties the sum and the sum of squares of all elements for any T will be calculated respectively.