142

Is there a way to determine whether or not a given .Net Type is a number? For example: System.UInt32/UInt16/Double are all numbers. I want to avoid a long switch-case on the Type.FullName.

Konamiman
  • 49,681
  • 17
  • 108
  • 138
Adi Barda
  • 3,259
  • 11
  • 32
  • 33
  • 2
    Duplicate of http://stackoverflow.com/questions/1130698/ and very close to some others. – H H Nov 17 '09 at 18:22
  • possible duplicate of [Using .Net, how can I determine if a type is a Numeric ValueType?](http://stackoverflow.com/questions/124411/using-net-how-can-i-determine-if-a-type-is-a-numeric-valuetype) – nawfal Dec 26 '13 at 15:38

22 Answers22

146

Try this:

Type type = object.GetType();
bool isNumber = (type.IsPrimitiveImple && type != typeof(bool) && type != typeof(char));

The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double,and Single.

Taking Guillaume's solution a little further:

public static bool IsNumericType(this object o)
{   
  switch (Type.GetTypeCode(o.GetType()))
  {
    case TypeCode.Byte:
    case TypeCode.SByte:
    case TypeCode.UInt16:
    case TypeCode.UInt32:
    case TypeCode.UInt64:
    case TypeCode.Int16:
    case TypeCode.Int32:
    case TypeCode.Int64:
    case TypeCode.Decimal:
    case TypeCode.Double:
    case TypeCode.Single:
      return true;
    default:
      return false;
  }
}

Usage:

int i = 32;
i.IsNumericType(); // True

string s = "Hello World";
s.IsNumericType(); // False
Community
  • 1
  • 1
Philip Wallace
  • 7,905
  • 3
  • 28
  • 40
  • The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single. – Yuriy Faktorovich Nov 17 '09 at 16:25
  • @Luke - good question. It's a struct - so you need to define numeric. – Philip Wallace Nov 17 '09 at 16:27
  • 3
    This would need to be re-engineered for the new numeric types in .NET 4.0 which don't have type codes. – Jon Skeet Nov 27 '09 at 17:43
  • 16
    How can you downvote me on an answer based on current technology. Maybe in .NET 62, int will be removed - are you going to downvote all answers with int? – Philip Wallace Nov 27 '09 at 19:09
  • @JonSkeet, What does `GetTypeCode` return for those new numeric types? The [docs](https://msdn.microsoft.com/en-us/library/system.type.gettypecode(v=vs.110).aspx) suggest `null`, but it isn't clear to me because I don't really know what the new numeric types are. – kdbanman Jul 17 '15 at 19:48
  • @kdbanman: The numeric types are in System.Numerics. (They're Complex and BigInteger.) Give it a try :) – Jon Skeet Jul 17 '15 at 19:49
  • @kdbanman It couldn't be `null`, `TypeCode` is a value type. Kinda obvious it is object, as it should be used for any other type not represented by the enum. – IS4 Aug 01 '15 at 19:27
  • 2
    If you would want to check if a nullable type is 'numeric' using this method, you can use: `var type = o.GetType().GetEnumUnderlyingType() ?? o.GetType();` Then switch on the type: `switch (Type.GetTypeCode(type))` – Robert Sirre Jul 06 '18 at 09:15
  • 1
    * correction: use `Nullable.GetUnderlyingType` instead of `.GetEnumUnderlyingType` – Robert Sirre Jul 06 '18 at 09:21
  • Enum types resolve to Int32 using this, which may be a false positive depending on your use case. – mhapps Oct 14 '22 at 14:42
111

Don't use a switch - just use a set:

HashSet<Type> NumericTypes = new HashSet<Type>
{
    typeof(decimal), typeof(byte), typeof(sbyte),
    typeof(short), typeof(ushort), ...
};

EDIT: One advantage of this over using a type code is that when new numeric types are introduced into .NET (e.g. BigInteger and Complex) it's easy to adjust - whereas those types won't get a type code.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 4
    and how you would use the HashSet? – RvdK Nov 17 '09 at 16:21
  • 9
    NumericTypes.Contains(whatever)? – mqp Nov 17 '09 at 16:23
  • 5
    bool isANumber = NumericTypes.Contains(classInstance.GetType()); – Yuriy Faktorovich Nov 17 '09 at 16:23
  • Would have thought the compiler would do an implicit convertion of the switch-statement to hashset. – Rolf Kristensen Aug 05 '13 at 15:02
  • 6
    @RolfKristensen: Well `switch` simply doesn't work on `Type`, so you can't. You can switch on `TypeCode` of course, but that's a different matter. – Jon Skeet Aug 05 '13 at 15:06
  • Another advantage of not using the type code is that no additional code, other than reference equality, must be run. The `typeof` operator is resolved at compile-time. – Mohammad Dehghan Jan 18 '23 at 09:49
  • Just don't use it multi-threaded because it can cause deadlocks. – Jeff Fischer Mar 30 '23 at 03:37
  • 1
    @JeffFischer: I *believe* that once constructed, a `HashSet` is safe for multi-threaded *read* operations, but you shouldn't mutate it from multiple threads. If you need a thread-safe set for all kinds of operations, you could use `ConcurrentDictionary`, or I suspect there are third-party collections available. – Jon Skeet Mar 30 '23 at 05:40
  • Thanks, @JonSkeet, I don't believe this is necessarily common knowledge as I've had to resolve multiple performance bugs caused by this structure used in multi-threaded scenarios. https://developercommunity.visualstudio.com/t/endless-loop-inside-unsafe-hashset-usage-in-logext/1354222 – Jeff Fischer Mar 30 '23 at 22:13
82

None of the solutions takes Nullable into account.

I modified Jon Skeet's solution a bit:

    private static HashSet<Type> NumericTypes = new HashSet<Type>
    {
        typeof(int),
        typeof(uint),
        typeof(double),
        typeof(decimal),
        ...
    };

    internal static bool IsNumericType(Type type)
    {
        return NumericTypes.Contains(type) ||
               NumericTypes.Contains(Nullable.GetUnderlyingType(type));
    }

I know I could just add the nullables itself to my HashSet. But this solution avoid the danger of forgetting to add a specific Nullable to your list.

    private static HashSet<Type> NumericTypes = new HashSet<Type>
    {
        typeof(int),
        typeof(int?),
        ...
    };
Jürgen Steinblock
  • 30,746
  • 24
  • 119
  • 189
  • 2
    Is a nullable type really numeric? Null is not a number, to my knowledge. – IS4 May 28 '14 at 23:29
  • 3
    That depends on what you want to achieve. In my case I needed to include nullables, too. But I also could think of situations where this is not a desired behaviour. – Jürgen Steinblock May 29 '14 at 05:23
  • Good! To treat nullable number as number is very useful in UI input validation. – guogangj Mar 22 '18 at 02:24
  • 1
    @IllidanS4 The check is on *Type* not the value. In most cases Nullable numeric types should be treated as numeric. Of course if the check was on value and value is null, then yes it shouldnt be considered numeric. – nawfal Nov 16 '18 at 05:47
40
public static bool IsNumericType(Type type)
{
  switch (Type.GetTypeCode(type))
  {
    case TypeCode.Byte:
    case TypeCode.SByte:
    case TypeCode.UInt16:
    case TypeCode.UInt32:
    case TypeCode.UInt64:
    case TypeCode.Int16:
    case TypeCode.Int32:
    case TypeCode.Int64:
    case TypeCode.Decimal:
    case TypeCode.Double:
    case TypeCode.Single:
      return true;
    default:
      return false;
  }
}

Note about optimization removed (see enzi comments) And if you really want to optimize it (losing readability and some safety...):

public static bool IsNumericType(Type type)
{
  TypeCode typeCode = Type.GetTypeCode(type);
  //The TypeCode of numerical types are between SByte (5) and Decimal (15).
  return (int)typeCode >= 5 && (int)typeCode <= 15;
}

Glenn Slayden
  • 17,543
  • 3
  • 114
  • 108
Guillaume
  • 12,824
  • 3
  • 40
  • 48
  • 14
    I know this answer is old, but I recently came across such a switch: do not use the suggested optimization! I looked at the IL code generated from such a switch, and noted that the compiler already applies the optimization (in IL 5 is subtracted from the type code and then values from 0 to 10 are considered true). Hence the switch should be used for it's more readable and safer and just as fast. – enzi Mar 12 '15 at 15:23
  • 2
    If you actually want to optimize it and don't care about readability the optimal code would be `return unchecked((uint)Type.GetTypeCode(type) - 5u) <= 10u;` thus removing the branch introduced by `&&`. – AnorZaken Apr 07 '17 at 14:40
24

Basically Skeet's solution but you can reuse it with Nullable types as follows:

public static class TypeHelper
{
    private static readonly HashSet<Type> NumericTypes = new HashSet<Type>
    {
        typeof(int),  typeof(double),  typeof(decimal),
        typeof(long), typeof(short),   typeof(sbyte),
        typeof(byte), typeof(ulong),   typeof(ushort),  
        typeof(uint), typeof(float)
    };
    
    public static bool IsNumeric(this Type myType)
    {
       return NumericTypes.Contains(Nullable.GetUnderlyingType(myType) ?? myType);
    }
}

Usage Examples:

//static invocation
int someNumber = 5;
TypeHelper.IsNumeric(typeof(someNumber)); //true
string someText = "test";
TypeHelper.IsNumeric(typeof(someText)); //false

//invoke via extension method
typeof(decimal).IsNumeric(); // true
typeof(string).IsNumeric(); // false
happs
  • 139
  • 2
  • 9
arviman
  • 5,087
  • 41
  • 48
11

Approach based on Philip's proposal, enhanced with SFun28's inner type check for Nullable types:

public static class IsNumericType
{
    public static bool IsNumeric(this Type type)
    {
        switch (Type.GetTypeCode(type))
        {
            case TypeCode.Byte:
            case TypeCode.SByte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
            case TypeCode.Decimal:
            case TypeCode.Double:
            case TypeCode.Single:
                return true;
            case TypeCode.Object:
                if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    return Nullable.GetUnderlyingType(type).IsNumeric();
                    //return IsNumeric(Nullable.GetUnderlyingType(type));
                }
                return false;
            default:
                return false;
        }
    }
}

Why this? I had to check if a given Type type is a numeric type, and not if an arbitrary object o is numeric.

Community
  • 1
  • 1
cimnine
  • 3,987
  • 4
  • 33
  • 49
6

With C# 7 this method gives to me better performance than switch case on TypeCode and HashSet<Type>:

public static bool IsNumeric(this object o) => o is byte || o is sbyte || o is ushort || o is uint || o is ulong || o is short || o is int || o is long || o is float || o is double || o is decimal;

Tests are following:

public static class Extensions
{
    public static HashSet<Type> NumericTypes = new HashSet<Type>()
    {
        typeof(byte), typeof(sbyte), typeof(ushort), typeof(uint), typeof(ulong), typeof(short), typeof(int), typeof(long), typeof(decimal), typeof(double), typeof(float)
    };

    public static bool IsNumeric1(this object o) => NumericTypes.Contains(o.GetType());

    public static bool IsNumeric2(this object o) => o is byte || o is sbyte || o is ushort || o is uint || o is ulong || o is short || o is int || o is long || o is decimal || o is double || o is float;

    public static bool IsNumeric3(this object o)
    {
        switch (o)
        {
            case Byte b:
            case SByte sb:
            case UInt16 u16:
            case UInt32 u32:
            case UInt64 u64:
            case Int16 i16:
            case Int32 i32:
            case Int64 i64:
            case Decimal m:
            case Double d:
            case Single f:
                return true;
            default:
                return false;
        }
    }

    public static bool IsNumeric4(this object o)
    {
        switch (Type.GetTypeCode(o.GetType()))
        {
            case TypeCode.Byte:
            case TypeCode.SByte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
            case TypeCode.Decimal:
            case TypeCode.Double:
            case TypeCode.Single:
                return true;
            default:
                return false;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {           
        var count = 100000000;

        //warm up calls
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric1();
        }
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric2();
        }
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric3();
        }
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric4();
        }

        //Tests begin here
        var sw = new Stopwatch();
        sw.Restart();
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric1();
        }
        sw.Stop();

        Debug.WriteLine(sw.ElapsedMilliseconds);

        sw.Restart();
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric2();
        }
        sw.Stop();

        Debug.WriteLine(sw.ElapsedMilliseconds);

        sw.Restart();
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric3();
        }
        sw.Stop();

        Debug.WriteLine(sw.ElapsedMilliseconds);

        sw.Restart();
        for (var i = 0; i < count; i++)
        {
            i.IsNumeric4();
        }
        sw.Stop();

        Debug.WriteLine(sw.ElapsedMilliseconds);
    }
Hugo Freitas
  • 101
  • 1
  • 3
6

Type extension with null-type support.

public static bool IsNumeric(this Type type)
    {
        if (type == null) { return false; }

        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            type = type.GetGenericArguments()[0];
        }

        switch (Type.GetTypeCode(type))
        {
            case TypeCode.Byte:
            case TypeCode.SByte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
            case TypeCode.Decimal:
            case TypeCode.Double:
            case TypeCode.Single:
                return true;
            default:
                return false;
        }
    }
5

Modified skeet's and arviman's solution utilizing Generics, and C# v7.0.

Edit: Came back many moons later to improve the code quality.

using System;
using System.Collections.Generic;
using System.Numerics;

public static class GenericTypeExtensions
{
    private static readonly HashSet<Type> _numericTypes = new HashSet<Type>
    {
        typeof(int), typeof(double), typeof(decimal),
        typeof(long), typeof(short), typeof(sbyte),
        typeof(byte), typeof(ulong), typeof(ushort),
        typeof(uint), typeof(float), typeof(BigInteger)
    };

    public static bool IsNumeric<T>(this T input)
    {
        if (input is null) return false;

        return _numericTypes.Contains(typeof(T));
    }

    public static bool IsNumericAtRuntime<T>(this T input)
    {
        if (input is null) return false;

        return _numericTypes.Contains(input.GetType());
    }

    /// <summary>
    /// Identifies whether or not this object is a numeric or nullable numeric type.
    /// <para>Examples</para>
    /// <para />int value = 0; true
    /// <para />var objValue = (object)(int)0; true
    /// <para />int? value = 0; true
    /// <para />int? value = null; true
    /// <para />var objValue = (object)(int?)0; true
    /// <para />var objValue = (object)(int?)(null); false - because (int?) is totally lost.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="input"></param>
    /// <returns></returns>
    public static bool IsNullableNumeric<T>(this T input)
    {
        if (input is null)
        {
            return _numericTypes.Contains(Nullable.GetUnderlyingType(typeof(T))); // see what the inner base type is
        }

        return _numericTypes.Contains(input.GetType());
    }

    public static void AddCustomNumericType<T>(this T _) where T : IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable
    {
        _numericTypes.Add(typeof(T));
    }

    public static bool TryAddCustomNumeric<T>(T input)
    {
        Type type;
        if (input is null)
        {
            type = Nullable.GetUnderlyingType(typeof(T));
            if (type is null) return false;
        }
        else
        { type = input.GetType(); }

        if (_numericTypes.Contains(type)) return true;

        var interfaces = type.GetInterfaces();
        var count = 0;

        for (var i = 0; i < interfaces.Length; i++)
        {
            switch(interfaces[i])
            {
                case IComparable:
                case IComparable<T>:
                case IConvertible:
                case IEquatable<T>:
                case IFormattable:
                    count++;
                    break;
                default: continue;
            }
        }

        if (count != 5) return false;

        _numericTypes.Add(type);
        return true;
    }

    public static bool TryAddCustomNumericType<T>(Type type)
    {
        if (type is null) return false;

        if (_numericTypes.Contains(type)) return true;

        var interfaces = type.GetInterfaces();
        var count = 0;

        for (var i = 0; i < interfaces.Length; i++)
        {
            switch (interfaces[i])
            {
                case IComparable:
                case IComparable<T>:
                case IConvertible:
                case IEquatable<T>:
                case IFormattable:
                    count++;
                    break;
                default: continue;
            }
        }

        if (count != 5) return false;

        _numericTypes.Add(type);
        return true;
    }

Examples/Unit Tests Pay attention to the Assert.True/False flip.

public class IsNumericTests
{
    [Fact]
    public void IsNumeric()
    {
        var value = 0;

        Assert.True(value.IsNumeric());
    }

    [Fact]
    public void IsObjectNumeric()
    {
        var value = 0;
        var objValue = (object)value;

        Assert.False(objValue.IsNumeric());
    }

    [Fact]
    public void IsNumericAtRuntime()
    {
        var value = 0;

        Assert.True(value.IsNumericAtRuntime());
    }

    [Fact]
    public void IsObjectNumericAtRuntime()
    {
        var value = 0;
        var objValue = (object)value;

        Assert.True(objValue.IsNumericAtRuntime());
    }

    [Fact]
    public void IsNullableNumeric()
    {
        int? value = 0;

        Assert.True(value.IsNullableNumeric());
    }

    [Fact]
    public void IsNullableNumericAsObject()
    {
        int? value = 0;
        var objValue = (object)value;

        Assert.True(objValue.IsNullableNumeric());
    }

    [Fact]
    public void IsNullableNumericWhenNull()
    {
        int? value = null;

        Assert.True(value.IsNullableNumeric());
    }

    [Fact]
    public void IsNullableNumericWhenNullAsObject()
    {
        int? value = null;
        var objValue = (object)value;

        Assert.False(objValue.IsNullableNumeric());
    }

    [Fact]
    public void IsNullableNumericWhenNotNumber()
    {
        string value = "test";

        Assert.False(value.IsNullableNumeric());
    }

    [Fact]
    public void IsNullableNumericWhenNullAndNotNumber()
    {
        Type? value = null;

        Assert.False(value.IsNullableNumeric());
    }
}

Benchmarks/Performance

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using HouseofCat.Extensions;
using System;

[MarkdownExporterAttribute.GitHub]
[MemoryDiagnoser]
[SimpleJob(runtimeMoniker: RuntimeMoniker.Net50 | RuntimeMoniker.NetCoreApp31)]
public class IsNumericBenchmark
{
    public int IntValue = 1;
    public long? LongValue = int.MaxValue;

    public object ObjIntValue => (object)IntValue;
    public object ObjLongValue => (object)LongValue;

    [Benchmark(Baseline = true)]
    public void IsNumeric()
    {
        IntValue.IsNumeric();
    }

    [Benchmark]
    public void IsNumericAtRuntime()
    {
        ObjIntValue.IsNumericAtRuntime();
    }

    [Benchmark]
    public void IsNullableNumeric()
    {
        LongValue.IsNullableNumeric();
    }
}

BenchmarkDotNet=v0.13.0, OS=Windows 10.0.18363.1621 (1909/November2019Update/19H2)
Intel Core i7-9850H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores
.NET SDK=5.0.400-preview.21277.10
  [Host]   : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT
  .NET 5.0 : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT

Job=.NET 5.0  Runtime=.NET 5.0  

Method Mean Error StdDev Ratio RatioSD Gen 0 Gen 1 Gen 2 Allocated
IsNumeric 16.65 ns 0.104 ns 0.087 ns 1.00 0.00 - - - -
IsNumericAtRuntime 19.26 ns 0.409 ns 0.383 ns 1.16 0.02 0.0038 - - 24 B
IsNullableNumeric 58.65 ns 0.692 ns 0.647 ns 3.53 0.04 0.0038 - - 24 B

HouseofCat/Tesseract Github Repo & Nuget

HouseCat
  • 1,559
  • 20
  • 22
4

Try the TypeSupport nuget package for C#. It has support for detecting all numeric types (among many other features):

var extendedType = typeof(int).GetExtendedType();
Assert.IsTrue(extendedType.IsNumericType);
Michael Brown
  • 1,585
  • 1
  • 22
  • 36
  • I didn't know this package. Seems to be a life savior in many cases to avoid to write our own code for the kind of operations requested by the OP. Thanks ! – AFract Jan 03 '20 at 10:06
  • that is a great library. I presume you're the author? You should attribute that. – Liam Mar 19 '21 at 16:54
4

.NET 7 (in preview 5 at time of writing) is set to introduce the INumeric<> interface which will be implemented by 20 built-in types.

Checking for this interface is likely to be future proof when additional numeric types are added.

static bool IsNumeric(object o){
    var numType = typeof(INumber<>);
    return o.GetType().GetInterfaces().Any(iface =>
        iface.IsGenericType && (iface.GetGenericTypeDefinition() == numType));
}

The types currently implementing this in the .Net 7 preview are:

byte
char
decimal
double
Half
short
int
long
Int128
nint
BigInteger
Complex
NFloat
sbyte
float
ushort
uint
ulong
UInt128
nuint
codybartfast
  • 7,323
  • 3
  • 21
  • 25
3

Just to add to the other answers that were looking at the TypeCode - you can simplify even further if you wish, to avoid the long switch statements:

    public static bool IsNumeric(this Type type)
    {
        var typeCode = (int)Type.GetTypeCode(type);
        return typeCode > 4 && typeCode < 16;
    }

    public static bool IsNumeric(this object source)
    {
        return source.GetType().IsNumeric();
    }
abagonhishead
  • 300
  • 3
  • 6
2

You could use Type.IsPrimitive and then sort out the Boolean and Char types, something like this:

bool IsNumeric(Type type)
{
    return type.IsPrimitive && type!=typeof(char) && type!=typeof(bool);
}

EDIT: You may want to exclude the IntPtr and UIntPtr types as well, if you don't consider them to be numeric.

Konamiman
  • 49,681
  • 17
  • 108
  • 138
2
bool IsNumeric(Type type)
    => type == typeof(decimal)
    || type == typeof(int) 
    || type == typeof(uint)
    || type == typeof(long)
    || type == typeof(ulong)
    || type == typeof(short)
    || type == typeof(ushort)
    || type == typeof(byte)
    || type == typeof(sbyte)
    || type == typeof(float)
    || type == typeof(double)
    ;
1

EDIT: Well, I modified the code below to be more performant and then ran the tests posted by @Hugo against it. The speeds are about on par with @Hugo's IF using the last item in his sequence (Decimal). If however using the first item 'byte' his takes the cake, but clearly order matters when it comes to performance. Although using the code below is easier to write and more consistent on it's cost, it is not, however, maintainable or future proof-able.

Looks like switching from Type.GetTypeCode() to Convert.GetTypeCode() sped up performance drastically, about 25%, VS Enum.Parse() which was like 10 times slower.


I know this post is old but IF using the TypeCode enum method, easiest (and probably the cheapest) would be something like this:

public static bool IsNumericType(this object o)
{   
  var t = (byte)Convert.GetTypeCode(o);
  return t > 4 && t < 16;
}

Given the following enum definition for TypeCode:

public enum TypeCode
{
    Empty = 0,
    Object = 1,
    DBNull = 2,
    Boolean = 3,
    Char = 4,
    SByte = 5,
    Byte = 6,
    Int16 = 7,
    UInt16 = 8,
    Int32 = 9,
    UInt32 = 10,
    Int64 = 11,
    UInt64 = 12,
    Single = 13,
    Double = 14,
    Decimal = 15,
    DateTime = 16,
    String = 18
}

I haven't tested it thoroughly, but for basic C# numeric types, this would seem to cover it. However, as @JonSkeet mentioned, this enum isn't updated for additional types added to .NET down the road.

Hector Bas
  • 31
  • 6
1

Switch is a little slow, bacause every time methods in the worst situation will be go through all type. I think, using Dictonary is more nice, in this situation you will be have O(1):

public static class TypeExtensions
{
    private static readonly HashSet<Type> NumberTypes = new HashSet<Type>();

    static TypeExtensions()
    {
        NumberTypes.Add(typeof(byte));
        NumberTypes.Add(typeof(decimal));
        NumberTypes.Add(typeof(double));
        NumberTypes.Add(typeof(float));
        NumberTypes.Add(typeof(int));
        NumberTypes.Add(typeof(long));
        NumberTypes.Add(typeof(sbyte));
        NumberTypes.Add(typeof(short));
        NumberTypes.Add(typeof(uint));
        NumberTypes.Add(typeof(ulong));
        NumberTypes.Add(typeof(ushort));
    }

    public static bool IsNumber(this Type type)
    {
        return NumberTypes.Contains(type);
    }
}
Smagin Alexey
  • 305
  • 2
  • 6
1

With the advent of C#11 and Dotnet 7 we have Generic Maths!

You can just check whether or not a type implements the INumber<T> at which point you can do everything you'd expect to be able to on a number.

To do this, you can use typeof(type).isAssignableTo(typeof(INumber<>)).

For example, to retreive all framework provided numeric types, you can run:

List<Type> numericTypes = typeof(Type).Assembly.GetTypes()
                                      .Where(t => t.IsAssignableTo(typeof(INumber<>)))
                                      .ToList();
ScottishTapWater
  • 3,656
  • 4
  • 38
  • 81
1

Based on codybartfast answer using .NET7 and System.Numerics, actually, this extension method can be the solution

public static bool IsNumeric(this Type type)
{
    var numType = typeof(INumber<>);
    var result = type.GetInterfaces().Any(i => i.IsGenericType && (i.GetGenericTypeDefinition() == numType));
    return result;
}
Noschema
  • 31
  • 6
0

This may work as well. However, you may want to follow it up with a Type.Parse to cast it the way you want it afterwards.

public bool IsNumeric(object value)
{
    float testValue;
    return float.TryParse(value.ToString(), out testValue);
}
DaveT
  • 9
  • 1
-1

Unfortunately these types don't have much in common other than they are all value types. But to avoid a long switch-case you could just define a readonly list with all these types and then just check if the given type is inside the list.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
-1

They are all value types (except for bool and maybe enum). So you could simply use:

bool IsNumberic(object o)
{
    return (o is System.ValueType && !(o is System.Boolean) && !(o is System.Enum))
}
MandoMando
  • 5,215
  • 4
  • 28
  • 35
-2

oops! Misread the question! Personally, would roll with Skeet's.


hrm, sounds like you want to DoSomething on Type of your data. What you could do is the following

public class MyClass
{
    private readonly Dictionary<Type, Func<SomeResult, object>> _map = 
        new Dictionary<Type, Func<SomeResult, object>> ();

    public MyClass ()
    {
        _map.Add (typeof (int), o => return SomeTypeSafeMethod ((int)(o)));
    }

    public SomeResult DoSomething<T>(T numericValue)
    {
        Type valueType = typeof (T);
        if (!_map.Contains (valueType))
        {
            throw new NotSupportedException (
                string.Format (
                "Does not support Type [{0}].", valueType.Name));
        }
        SomeResult result = _map[valueType] (numericValue);
        return result;
    }
}
Community
  • 1
  • 1
johnny g
  • 3,533
  • 1
  • 25
  • 40