C # - cách xác định Loại có phải là số hay không


105

Có cách nào để xác định xem .Net Type đã cho có phải là một số hay không? Ví dụ: System.UInt32/UInt16/Doubleđều là số. Tôi muốn tránh trường hợp chuyển đổi dài trên Type.FullName.


4
Dupe của nhiều, nhiều, nhiều. Tại sao điều này vẫn chưa được đóng lại?
Noldorin

2
Bản sao của stackoverflow.com/questions/1130698 và rất gần với một số khác.
Henk Holterman

Câu trả lời:


110

Thử cái này:

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

Các kiểu nguyên thủy là Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double và Single.

Xem xét giải pháp của Guillaume xa hơn một chút:

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;
  }
}

Sử dụng:

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

string s = "Hello World";
s.IsNumericType(); // False

2
Vì vậy, decimalloại không phải là số?
LukeH

2
@Xaero: Tôi không nghi ngờ gì nữa, đó decimal số. Chỉ vì nó không phải là một nguyên thủy không có nghĩa là nó không phải là số. Mã của bạn cần phải giải thích cho điều này.
LukeH

2
Điều này sẽ cần được thiết kế lại cho các kiểu số mới trong .NET 4.0 không có mã kiểu.
Jon Skeet

7
Làm thế nào bạn có thể phản đối tôi về một câu trả lời dựa trên công nghệ hiện tại. Có thể trong .NET 62, int sẽ bị loại bỏ - bạn sẽ phản đối tất cả các câu trả lời với int?
Philip Wallace

1
@DiskJunky Xin lỗi bạn. Đó là gần ba năm trước và tôi không nhớ nội dung của họ là gì.
kdbanman

93

Không sử dụng công tắc - chỉ sử dụng một bộ:

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

CHỈNH SỬA: Một ưu điểm của điều này so với việc sử dụng mã kiểu là khi các kiểu số mới được đưa vào .NET (ví dụ: BigIntegerComplex ), bạn sẽ dễ dàng điều chỉnh - trong khi những kiểu đó sẽ không nhận được mã kiểu.


4
và bạn sẽ sử dụng HashSet như thế nào?
RvdK

8
NumericTypes.Contains (bất cứ điều gì)?
mqp

3
bool isANumber = NumericTypes.Contains (classInstance.GetType ());
Yuriy Faktorovich

Có thể nghĩ rằng trình biên dịch sẽ thực hiện một sự chuyển đổi ngầm định của câu lệnh switch thành hashset.
Rolf Kristensen

6
@RolfKristensen: switchĐơn giản là nó không hoạt động Type, vì vậy bạn không thể. TypeCodeTất nhiên, bạn có thể bật , nhưng đó là một vấn đề khác.
Jon Skeet

69

Không có giải pháp nào tính đến Nullable.

Tôi đã sửa đổi giải pháp của Jon Skeet một chút:

    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));
    }

Tôi biết tôi chỉ có thể thêm chính các nullables vào HashSet của mình. Nhưng giải pháp này tránh nguy cơ quên thêm một Nullable cụ thể vào danh sách của bạn.

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

2
Kiểu nullable có thực sự là số không? Null không phải là một con số, theo hiểu biết của tôi.
IllidanS4 muốn Monica trở lại

2
Điều đó phụ thuộc vào những gì bạn muốn đạt được. Trong trường hợp của tôi, tôi cũng cần bao gồm nullable. Nhưng tôi cũng có thể nghĩ đến những tình huống mà đây không phải là một hành vi mong muốn.
Jürgen Steinblock

Tốt! Để coi số nullable là số rất hữu ích trong xác thực đầu vào UI.
guogangj

1
@ IllidanS4 Kiểm tra ở Loại không phải giá trị. Trong hầu hết các trường hợp, kiểu số Nullable phải được coi là số. Tất nhiên nếu kiểm tra là giá trị và giá trị là null, thì có, nó sẽ không được coi là số.
nawfal

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;
  }
}

Lưu ý về việc tối ưu hóa đã bị xóa (xem phần nhận xét trên enzi) Và nếu bạn thực sự muốn tối ưu hóa nó (mất khả năng đọc và một số an toàn ...):

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;
}


13
Tôi biết câu trả lời này đã cũ, nhưng gần đây tôi đã gặp một chuyển đổi như vậy: không sử dụng tối ưu hóa được đề xuất! Tôi đã xem mã IL được tạo từ một công tắc như vậy và lưu ý rằng trình biên dịch đã áp dụng tối ưu hóa (trong IL 5 được trừ khỏi mã loại và sau đó các giá trị từ 0 đến 10 được coi là đúng). Do đó, công tắc nên được sử dụng để dễ đọc hơn, an toàn hơn và nhanh chóng.
enzi

1
Nếu bạn thực sự muốn tối ưu hóa nó và không quan tâm đến khả năng đọc, mã tối ưu sẽ return unchecked((uint)Type.GetTypeCode(type) - 5u) <= 10u;do đó loại bỏ nhánh được giới thiệu bởi &&.
AnorZaken

14

Về cơ bản giải pháp của Skeet nhưng bạn có thể sử dụng lại nó với các kiểu Nullable như sau:

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(Type myType)
    {
       return NumericTypes.Contains(Nullable.GetUnderlyingType(myType) ?? myType);
    }
}

9

Phương pháp tiếp cận dựa trên đề xuất của Philip , được cải tiến với tính năng kiểm tra loại bên trong của SFun28 cho Nullablecác loại:

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;
        }
    }
}

Tại sao là cái này? Tôi phải kiểm tra xem Type typemột số đã cho có phải là kiểu số hay không, và không nếu một tùy ý object olà số.


4

Với C # 7, phương pháp này mang lại cho tôi hiệu suất tốt hơn so với việc bật trường hợp chuyển đổi TypeCodeHashSet<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;

Các thử nghiệm sau:

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);
    }

3

Bạn có thể sử dụng Type.IsPrimitive và sau đó sắp xếp BooleanChar loại loại, tương tự như sau:

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

EDIT : Bạn có thể muốn loại trừ IntPtrUIntPtrloại là tốt, nếu bạn không xem xét họ là số.


1
Vì vậy, decimalloại không phải là số?
LukeH

Rất tiếc ... chà, có vẻ như giải pháp của Guillaume là tốt nhất sau tất cả.
Konamiman

3

Nhập phần mở rộng với hỗ trợ kiểu null.

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;
        }
    }

1

Câu trả lời ngắn gọn: Không.

Câu trả lời dài hơn: Không.

Thực tế là nhiều kiểu khác nhau trong C # có thể chứa dữ liệu số. Trừ khi bạn biết điều gì sẽ xảy ra (Int, Double, v.v.), bạn cần sử dụng trường hợp "dài".


1

Điều này cũng có thể hoạt động. Tuy nhiên, bạn có thể muốn theo dõi nó với Type.Parse để truyền nó theo cách bạn muốn sau đó.

public bool IsNumeric(object value)
{
    float testValue;
    return float.TryParse(value.ToString(), out testValue);
}

1

Sửa đổi của Skeet và giải pháp arviman của việc sử dụng Generics, ReflectionC# v6.0.

private static readonly HashSet<Type> m_numTypes = 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)
};

Theo dõi bởi:

public static bool IsNumeric<T>( this T myType )
{
    var IsNumeric = false;

    if( myType != null )
    {
        IsNumeric = m_numTypes.Contains( myType.GetType() );
    }

    return IsNumeric;
}

Sử dụng cho (T item):

if ( item.IsNumeric() ) {}

null trả về false.


1

Chuyển đổi hơi chậm, vì mọi phương pháp trong tình huống xấu nhất sẽ được thực hiện tất cả các loại. Tôi nghĩ, sử dụng Dictionary thì tốt hơn, trong tình huống này, bạn sẽ có 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);
    }
}

1

Hãy thử gói nuget TypeSupport cho C #. Nó có hỗ trợ phát hiện tất cả các kiểu số (trong số nhiều tính năng khác):

var extendedType = typeof(int).GetExtendedType();
Assert.IsTrue(extendedType.IsNumericType);

Tôi không biết gói này. Có vẻ như là một cứu tinh trong nhiều trường hợp để tránh phải viết mã của riêng chúng tôi cho loại hoạt động theo yêu cầu của OP. Cảm ơn !
AFract

0

Thật không may, những loại này không có nhiều điểm chung ngoài việc chúng đều là các loại giá trị. Nhưng để tránh trường hợp chuyển đổi dài, bạn chỉ có thể xác định danh sách chỉ đọc với tất cả các loại này và sau đó chỉ cần kiểm tra xem loại đã cho có nằm trong danh sách hay không.


0

Chúng đều là các kiểu giá trị (ngoại trừ bool và có thể là enum). Vì vậy, bạn có thể chỉ cần sử dụng:

bool IsNumberic(object o)
{
    return (o is System.ValueType && !(o is System.Boolean) && !(o is System.Enum))
}

1
Điều này sẽ trả về true cho bất kỳ người dùng nào do người dùng xác định struct... Tôi không nghĩ đó là điều bạn muốn.
Dan Tao

1
Bạn nói đúng. Các kiểu số dựng sẵn cũng là cấu trúc. Vì vậy, tốt hơn hãy đi với so sánh Nguyên thủy sau đó.
MandoMando

0

BIÊN TẬP: Chà, tôi đã sửa đổi mã bên dưới để hoạt động hiệu quả hơn và sau đó chạy các bài kiểm tra do @Hugo đăng để chống lại nó. Tốc độ ngang bằng với IF của @ Hugo bằng cách sử dụng mục cuối cùng trong chuỗi của anh ấy (Thập phân). Tuy nhiên, nếu sử dụng mục đầu tiên 'byte', anh ấy sẽ mất bánh, nhưng thứ tự rõ ràng là vấn đề quan trọng khi nói đến hiệu suất. Mặc dù sử dụng mã bên dưới dễ viết hơn và phù hợp hơn về chi phí, tuy nhiên, nó không phải là có thể bảo trì hoặc có thể kiểm chứng trong tương lai.

Có vẻ như việc chuyển từ Type.GetTypeCode () sang Convert.GetTypeCode () đã tăng tốc hiệu suất đáng kể, khoảng 25%, VS Enum.Parse () chậm hơn 10 lần.


Tôi biết bài đăng này đã cũ nhưng NẾU sử dụng phương pháp Enum TypeCode, đơn giản nhất (và có lẽ là rẻ nhất) sẽ là như thế này:

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

Đưa ra định nghĩa enum sau cho 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
}

Tôi đã không kiểm tra nó kỹ lưỡng, nhưng đối với các loại số C # cơ bản, điều này dường như sẽ che đậy nó. Tuy nhiên, như @JonSkeet đã đề cập, enum này không được cập nhật cho các loại bổ sung được thêm vào .NET.


-1

Giáo sư! Đọc sai câu hỏi! Cá nhân, sẽ cuộn với Skeet's .


HRM, âm thanh như bạn muốn DoSomethingtrên Typedữ liệu của bạn. Những gì bạn có thể làm là như sau

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;
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.