C # - วิธีตรวจสอบว่าประเภทเป็นตัวเลขหรือไม่


105

มีวิธีตรวจสอบว่า. Net Type เป็นตัวเลขหรือไม่? ตัวอย่างเช่น: System.UInt32/UInt16/Doubleเป็นตัวเลขทั้งหมด ฉันต้องการหลีกเลี่ยงกรณีสวิตช์ยาวในไฟล์Type.FullName.


4
ล่อลวงหลายหลายหลาย ทำไมถึงยังไม่ปิด?
Noldorin

2
สำเนาstackoverflow.com/questions/1130698และใกล้เคียงกับไฟล์อื่น ๆ
Henk Holterman

คำตอบ:


110

ลองสิ่งนี้:

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

ประเภทดั้งเดิม ได้แก่ Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double และ Single

ใช้วิธีแก้ปัญหาของ Guillaumeต่อไปอีกเล็กน้อย:

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

การใช้งาน:

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

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

2
ดังนั้นdecimalประเภทจึงไม่ใช่ตัวเลข?
LukeH

2
@ Xaero: ฉันไม่สงสัยเลยว่าdecimal มันเป็นตัวเลข เพียงเพราะมันไม่ใช่แบบดั้งเดิมไม่ได้หมายความว่ามันไม่ใช่ตัวเลข รหัสของคุณจำเป็นต้องคำนึงถึงสิ่งนี้
LukeH

2
สิ่งนี้จะต้องได้รับการออกแบบใหม่สำหรับประเภทตัวเลขใหม่ใน. NET 4.0 ซึ่งไม่มีรหัสประเภท
Jon Skeet

7
คุณจะลงคะแนนให้ฉันได้อย่างไรสำหรับคำตอบที่อิงจากเทคโนโลยีปัจจุบัน บางทีใน. NET 62, int จะถูกลบ - คุณจะลงคะแนนคำตอบทั้งหมดด้วย int หรือไม่?
Philip Wallace

1
@DiskJunky ขอโทษนะเพื่อน เกือบสามปีที่แล้วและฉันจำไม่ได้ว่าเนื้อหาของพวกเขาคืออะไร
kdbanman

94

อย่าใช้สวิตช์ - เพียงแค่ใช้ชุด:

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

แก้ไข: ข้อดีอย่างหนึ่งของสิ่งนี้ในการใช้รหัสประเภทคือเมื่อมีการนำประเภทตัวเลขใหม่เข้ามาใน. NET (เช่นBigIntegerและComplex ) จะปรับเปลี่ยนได้ง่ายในขณะที่ประเภทเหล่านั้นจะไม่ได้รับรหัสประเภท


4
และคุณจะใช้ HashSet อย่างไร?
RvdK

8
NumericTypes ประกอบด้วย (อะไรก็ได้)?
mqp

3
บูล isANumber = NumericTypes.Contains (classInstance.GetType ());
Yuriy Faktorovich

คงคิดว่าคอมไพเลอร์จะทำการแปลงคำสั่ง switch เป็น hashset โดยปริยาย
Rolf Kristensen

6
@RolfKristensen: switchก็ไม่ได้ผลTypeดังนั้นคุณจึงทำไม่ได้ แน่นอนคุณสามารถเปิดได้TypeCodeแต่นั่นเป็นเรื่องที่แตกต่างออกไป
Jon Skeet

70

ไม่มีโซลูชันใดที่คำนึงถึง Nullable

ฉันแก้ไขโซลูชันของ Jon Skeet เล็กน้อย:

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

ฉันรู้ว่าฉันสามารถเพิ่ม nullables ลงใน HashSet ของฉันได้ แต่วิธีนี้จะหลีกเลี่ยงอันตรายจากการลืมเพิ่ม Nullable เฉพาะในรายการของคุณ

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

2
ประเภทที่เป็นโมฆะเป็นตัวเลขจริงหรือ Null ไม่ใช่ตัวเลขสำหรับความรู้ของฉัน
IllidanS4 ต้องการให้ Monica กลับมาใน

2
ขึ้นอยู่กับสิ่งที่คุณต้องการบรรลุ ในกรณีของฉันฉันจำเป็นต้องรวมค่าว่างด้วย แต่ฉันยังสามารถนึกถึงสถานการณ์ที่นี่ไม่ใช่พฤติกรรมที่ต้องการ
Jürgen Steinblock

ดี! ในการจัดการกับ nullable number เป็น number มีประโยชน์มากในการตรวจสอบอินพุต UI
guogangj

1
@ IllidanS4 การตรวจสอบอยู่ในประเภทไม่ใช่ค่า ในกรณีส่วนใหญ่ประเภทตัวเลขที่เป็น Nullable ควรถือว่าเป็นตัวเลข แน่นอนว่าถ้าการตรวจสอบเป็นไปตามค่าและค่าเป็นโมฆะก็ไม่ควรถือเป็นตัวเลข
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;
  }
}

หมายเหตุเกี่ยวกับการเพิ่มประสิทธิภาพที่ถูกลบ (ดูความคิดเห็นของ enzi) และหากคุณต้องการเพิ่มประสิทธิภาพจริงๆ (สูญเสียความสามารถในการอ่านและความปลอดภัย ... ):

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
ฉันรู้ว่าคำตอบนี้เก่า แต่ฉันเพิ่งเจอสวิตช์ดังกล่าว: อย่าใช้การเพิ่มประสิทธิภาพที่แนะนำ! ฉันดูรหัส IL ที่สร้างจากสวิตช์ดังกล่าวและสังเกตว่าคอมไพลเลอร์ใช้การปรับให้เหมาะสมแล้ว (ใน IL 5 ถูกลบออกจากรหัสประเภทแล้วค่าจาก 0 ถึง 10 ถือว่าเป็นจริง) ดังนั้นจึงควรใช้สวิตช์เพื่อให้อ่านง่ายและปลอดภัยยิ่งขึ้นและเร็วเท่ากัน
enzi

1
ถ้าคุณต้องการจริงเพื่อเพิ่มประสิทธิภาพของมันและไม่สนใจเกี่ยวกับการอ่านรหัสที่ดีที่สุดจะเป็นจึงออกสาขาที่นำโดยreturn unchecked((uint)Type.GetTypeCode(type) - 5u) <= 10u; &&
AnorZaken

14

โดยพื้นฐานแล้วโซลูชันของ Skeet แต่คุณสามารถใช้ซ้ำได้กับ Nullable types ดังนี้:

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

แนวทางตามข้อเสนอของ Philipซึ่งปรับปรุงด้วยการตรวจสอบประเภทภายในของ SFun28สำหรับNullableประเภท:

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

ทำไมถึงเป็นเช่นนี้ ฉันต้องตรวจสอบว่าสิ่งที่ระบุType typeนั้นเป็นประเภทตัวเลขหรือไม่และไม่ใช่ว่าobject oตัวเลขนั้นเป็นตัวเลขหรือไม่


4

ด้วย C # 7 วิธีนี้ให้ประสิทธิภาพที่ดีกว่าการเปิดเคสTypeCodeและ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;

การทดสอบกำลังติดตาม:

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

คุณสามารถใช้Type.IsPrimitiveจากนั้นจัดเรียงBooleanและCharประเภทต่างๆดังนี้:

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

แก้ไข : คุณอาจต้องการยกเว้นIntPtrและUIntPtrประเภทด้วยหากคุณไม่ถือว่าเป็นตัวเลข


1
ดังนั้นdecimalประเภทจึงไม่ใช่ตัวเลข?
LukeH

อ๊ะ ... ดูเหมือนว่าวิธีแก้ปัญหาของ Guillaume จะดีที่สุด
Konamiman

3

พิมพ์นามสกุลด้วยการสนับสนุนประเภท 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 # สามารถมีข้อมูลตัวเลขได้ เว้นแต่คุณจะรู้ว่าจะเกิดอะไรขึ้น (Int, Double ฯลฯ ) คุณจำเป็นต้องใช้ case statement "long"


1

ซึ่งอาจใช้ได้ผลเช่นกัน อย่างไรก็ตามคุณอาจต้องการติดตามด้วย TypeParse เพื่อโยนมันในแบบที่คุณต้องการในภายหลัง

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

1

ดัดแปลงของเป้าบินและการแก้ไขปัญหา arviman ของใช้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)
};

ติดตามโดย:

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

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

    return IsNumeric;
}

การใช้งานสำหรับ(T item):

if ( item.IsNumeric() ) {}

null ส่งคืนเท็จ


1

สวิตช์ช้าเล็กน้อยเพราะทุกครั้งวิธีการในสถานการณ์ที่เลวร้ายที่สุดจะผ่านทุกประเภท ฉันคิดว่าการใช้ Dictonary นั้นดีกว่าในสถานการณ์นี้คุณจะมี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

ลองTypeSupportแพคเกจ nuget สำหรับ C # มีการสนับสนุนสำหรับการตรวจจับประเภทตัวเลขทั้งหมด (ท่ามกลางคุณสมบัติอื่น ๆ อีกมากมาย):

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

ฉันไม่รู้จักแพ็คเกจนี้ ดูเหมือนจะเป็นผู้ช่วยชีวิตในหลาย ๆ กรณีเพื่อหลีกเลี่ยงการเขียนโค้ดของเราเองสำหรับประเภทของการดำเนินการที่ OP ร้องขอ ขอบคุณ!
หลัง

0

น่าเสียดายที่ประเภทเหล่านี้ไม่มีอะไรเหมือนกันมากนักนอกจากประเภทมูลค่าทั้งหมด แต่เพื่อหลีกเลี่ยงการเปลี่ยนกรณีที่ยาวคุณสามารถกำหนดรายการแบบอ่านอย่างเดียวด้วยประเภทเหล่านี้ทั้งหมดจากนั้นตรวจสอบว่าประเภทที่กำหนดอยู่ในรายการหรือไม่


0

เป็นประเภทค่าทั้งหมด (ยกเว้นบูลและอาจเป็น enum) ดังนั้นคุณสามารถใช้:

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

1
สิ่งนี้จะคืนค่าจริงสำหรับผู้ใช้กำหนดstruct... ฉันไม่คิดว่านั่นคือสิ่งที่คุณต้องการ
ด่านเต่า

1
คุณถูก. ประเภทตัวเลขในตัวเป็นโครงสร้างเช่นกัน ดังนั้นควรไปเปรียบเทียบแบบดั้งเดิมดีกว่า
MandoMando

0

แก้ไข:ฉันแก้ไขโค้ดด้านล่างให้มีประสิทธิภาพมากขึ้นจากนั้นจึงทำการทดสอบที่โพสต์โดย @Hugo กับมัน ความเร็วใกล้เคียงกับ IF ของ @ Hugo โดยใช้รายการสุดท้ายในลำดับ (ทศนิยม) อย่างไรก็ตามหากใช้รายการแรก 'ไบต์' เขาจะใช้เค้ก แต่คำสั่งมีความสำคัญอย่างชัดเจนเมื่อพูดถึงประสิทธิภาพ แม้ว่าการใช้โค้ดด้านล่างจะเขียนได้ง่ายกว่าและมีความสอดคล้องกับต้นทุนมากขึ้น แต่ก็ไม่สามารถบำรุงรักษาได้หรือสามารถพิสูจน์ได้ในอนาคต

ดูเหมือนว่าการเปลี่ยนจาก Type.GetTypeCode () เป็น Convert.GetTypeCode () เร่งประสิทธิภาพขึ้นอย่างมากประมาณ 25% VS Enum.Parse () ซึ่งช้ากว่า 10 เท่า


ฉันรู้ว่าโพสต์นี้เก่า แต่ถ้าใช้เมธอด TypeCode enum ง่ายที่สุด (และอาจถูกที่สุด) จะเป็นดังนี้:

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

ให้นิยาม enum ต่อไปนี้สำหรับ 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
}

ฉันยังไม่ได้ทดสอบอย่างละเอียด แต่สำหรับประเภทตัวเลข C # พื้นฐานดูเหมือนจะครอบคลุม อย่างไรก็ตามตามที่ @JonSkeet กล่าวไว้ enum นี้ไม่ได้รับการอัปเดตสำหรับประเภทเพิ่มเติมที่เพิ่มเข้ามาใน. NET บนท้องถนน


-1

โอ๊ะ! อ่านคำถามผิด! ส่วนตัวจะม้วนกับเป้าของ


HRM, เสียงเหมือนคุณต้องการที่จะDoSomethingอยู่กับTypeข้อมูลของคุณ สิ่งที่คุณทำได้มีดังต่อไปนี้

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;
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.