ตรวจสอบว่าวัตถุเป็นตัวเลขใน C # หรือไม่


92

ฉันต้องการที่จะตรวจสอบว่าวัตถุเป็นจำนวนเพื่อที่.ToString()จะส่งผลให้สตริงที่มีตัวเลขและ+, -,.

เป็นไปได้ไหมโดยพิมพ์เช็คอิน. net (like:) if (p is Number)?

หรือฉันควรแปลงเป็นสตริงแล้วลองแยกวิเคราะห์เป็นสองเท่า?

อัปเดต:เพื่อชี้แจงว่าวัตถุของฉันคือ int, uint, float, double และอื่น ๆ ไม่ใช่สตริง ฉันกำลังพยายามสร้างฟังก์ชันที่จะทำให้วัตถุใด ๆ เป็นอนุกรมเป็น xml ดังนี้:

<string>content</string>

หรือ

<numeric>123.3</numeric>

หรือเพิ่มข้อยกเว้น


5
ดูเหมือนว่าคุณกำลังพยายามเขียน XmlSerializer ของคุณเอง - มีอะไรผิดปกติกับผู้ให้บริการรายหนึ่งโดย. NET- msdn.microsoft.com/en-us/library/… ?
RichardOD

2
คุณอาจสามารถแก้ไขปัญหาทั้งหมดนี้ได้โดยการกำหนดรูปแบบ XML ของคุณโดยใช้ XSD จากนั้นสร้างวัตถุที่คุณสามารถจัดลำดับข้อมูลของคุณโดยใช้เครื่องมือ XSD ที่จัดส่ง - msdn.microsoft.com/en-us/library/x6c1kb0s % 28VS.71% 29.aspx
Dexter

@RichardOD: ฉันสามารถใช้การทำให้เป็นอนุกรม xml เพื่อทำให้เป็นอนุกรมวัตถุ [] ได้หรือไม่ ฉันต้องการให้เรียกใช้ฟังก์ชัน Flash adobe.com/livedocs/flex/201/html/wwhelp/wwhimpl/common/html/…
Piotr Czapla

คำตอบ:


184

คุณจะต้องทำการตรวจสอบประเภทสำหรับตัวเลขพื้นฐานแต่ละประเภท

นี่คือวิธีการขยายที่ควรใช้งานได้:

public static bool IsNumber(this object value)
{
    return value is sbyte
            || value is byte
            || value is short
            || value is ushort
            || value is int
            || value is uint
            || value is long
            || value is ulong
            || value is float
            || value is double
            || value is decimal;
}

ซึ่งควรครอบคลุมประเภทตัวเลขทั้งหมด

อัปเดต

ดูเหมือนว่าคุณต้องการแยกวิเคราะห์ตัวเลขจากสตริงในช่วง deserialisation double.TryParseในกรณีนี้ก็จะอาจจะเพียงแค่จะดีที่สุดในการใช้งาน

string value = "123.3";
double num;
if (!double.TryParse(value, out num))
    throw new InvalidOperationException("Value is not a number.");

แน่นอนว่าสิ่งนี้จะไม่รองรับจำนวนเต็ม / ทศนิยมที่ยาวมาก แต่ถ้าเป็นกรณีนี้คุณก็แค่ต้องเพิ่มการเรียกอื่น ๆไปที่long.TryParse/ decimal.TryParse/ อะไรก็ได้


วัตถุของฉันคือ int, สั้น, uint, float, double หรืออย่างอื่นที่เป็นตัวเลข
Piotr Czapla

@Piotr: อ่าใช่ ดูเหมือนว่าฉันเข้าใจคุณผิด ดูคำตอบที่อัปเดตของฉัน
Noldorin

1
@Noldorin: จริงๆแล้วรหัสรุ่นก่อนหน้าของคุณก็ใช้ได้เช่นกัน เพียงเพิ่มการตรวจสอบค่าว่างและใช้ค่า ToString () จากนั้นคุณไม่จำเป็นต้องตรวจสอบประเภทตัวเลขทั้งหมด
Fredrik Mörk

1
@Noldorin เป็นเรื่องน่าเศร้าที่ทางออกที่ถูกต้องคือ verbose :(.
Piotr Czapla

1
@ โจ: อันที่จริงมันจะไม่สร้างความแตกต่างเนื่องจาก ToString จะใช้วัฒนธรรมปัจจุบันด้วย
Noldorin

36

นำมาจากบล็อกของ Scott Hanselman :

public static bool IsNumeric(object expression)
{
    if (expression == null)
    return false;

    double number;
    return Double.TryParse( Convert.ToString( expression
                                            , CultureInfo.InvariantCulture)
                          , System.Globalization.NumberStyles.Any
                          , NumberFormatInfo.InvariantInfo
                          , out number);
}

6
ปัญหาของวิธีนี้คือถ้าคุณส่งสตริงซึ่งดูเหมือนตัวเลขมันจะจัดรูปแบบ อาจจะโอเคสำหรับคนส่วนใหญ่ แต่มันก็เป็นการแสดงสำหรับฉัน
Rob Sedgwick

1
ปัญหาที่อาจเกิดขึ้นอีกประการหนึ่งคือคุณไม่สามารถแยกวิเคราะห์ค่าต่ำสุด / สูงสุดเป็นสองเท่าได้ double.Parse(double.MaxValue.ToString())ทำให้เกิดOverflowException. คุณสามารถแก้ไขได้โดยให้ตัวปรับเปลี่ยนแบบไปกลับ.ToString("R")ในกรณีนี้ แต่การโอเวอร์โหลดนั้นไม่สามารถใช้ได้Convert.ToString(...)เนื่องจากเราไม่ทราบประเภท ฉันรู้ว่านี่เป็นเรื่องเล็กน้อย แต่ฉันก็สะดุดในขณะที่เขียนการทดสอบสำหรับ.IsNumeric()ส่วนขยายของฉันเอง "วิธีแก้ปัญหา" ของฉันคือการเพิ่มประเภทการตรวจสอบ swtich ก่อนที่จะพยายามแยกวิเคราะห์อะไรดูคำตอบของฉันสำหรับคำถามนี้สำหรับรหัส
เบ็น

21

ใช้ประโยชน์จากคุณสมบัติ IsPrimitive เพื่อสร้างวิธีการขยายที่สะดวก:

public static bool IsNumber(this object obj)
{
    if (Equals(obj, null))
    {
        return false;
    }

    Type objType = obj.GetType();
    objType = Nullable.GetUnderlyingType(objType) ?? objType;

    if (objType.IsPrimitive)
    {
        return objType != typeof(bool) && 
            objType != typeof(char) && 
            objType != typeof(IntPtr) && 
            objType != typeof(UIntPtr);
    }

    return objType == typeof(decimal);
}

แก้ไข: แก้ไขตามความคิดเห็น Generics ถูกลบออกตั้งแต่ประเภทค่าของกล่อง. GetType () ยังรวมการแก้ไขสำหรับค่าที่เป็นโมฆะ


1
ส่วนยาสามัญไม่ได้ให้อะไรพิเศษที่นี่ใช่ไหม คุณเข้าถึง GetType () ซึ่งมีอยู่ในวัตถุเท่านั้น ...
Peter Lillevold

จะบันทึกการดำเนินการกล่องเดียวหากถูกเรียกใช้ประเภทค่า คิดว่าสามารถใช้ซ้ำได้
Kenan EK

1
ทำไมไม่ใช้ typeof (T) แทน obj.GetType ด้วยวิธีนี้คุณจะไม่ได้รับ NullReferenceException หากมีคนส่งผ่านประเภทการอ้างอิงที่เป็นโมฆะ คุณยังสามารถใส่ข้อ จำกัด ทั่วไปบน T เพื่อยอมรับเฉพาะประเภทค่า แน่นอนว่าคุณเริ่มมีข้อมูลมากมายในเวลารวบรวมหากคุณทำเช่นนั้น
Trillian

objectและstringไม่ใช่ประเภทดั้งเดิม
We Are All Monica

@jnylen: คำตอบนี้ค่อนข้างนานแล้ว ฉันเชื่อว่าฉันขุดบางอย่างขึ้นมาจากแหล่งที่มาของกรอบสะท้อนแสงในเวลานั้น แต่ใครจะบอกได้ในวันนี้ ...
Kenan EK

10

มีคำตอบที่ยอดเยี่ยมอยู่ด้านบน นี่คือโซลูชันแบบ all-in-one สามโอเวอร์โหลดสำหรับสถานการณ์ที่แตกต่างกัน

// Extension method, call for any object, eg "if (x.IsNumeric())..."
public static bool IsNumeric(this object x) { return (x==null ? false : IsNumeric(x.GetType())); }

// Method where you know the type of the object
public static bool IsNumeric(Type type) { return IsNumeric(type, Type.GetTypeCode(type)); }

// Method where you know the type and the type code of the object
public static bool IsNumeric(Type type, TypeCode typeCode) { return (typeCode == TypeCode.Decimal || (type.IsPrimitive && typeCode != TypeCode.Object && typeCode != TypeCode.Boolean && typeCode != TypeCode.Char)); }

พิจารณาเพิ่มการตรวจสอบว่าง
wiero

ไม่ต้องการการตรวจสอบโมฆะ - เป็นวิธีการขยายคุณไม่สามารถเรียกมันด้วยค่าว่างได้ แน่นอนว่าใครบางคนยังสามารถเรียกใช้เป็นฟังก์ชันปกติได้ แต่นั่นไม่ใช่วิธีการใช้ส่วนขยายที่คาดไว้
Mick Bruno

5
ฉันคิดว่าเราสามารถเรียกมันด้วยค่า null ได้ วัตถุ obj = null; obj.IsNumeric ();
wiero

ขอบคุณ Weiro ได้แก้ไขแล้ว ไม่ทราบว่าวิธีการขยายการโทรที่มีค่า null เป็นไปได้ แต่แน่นอนว่าเป็น!
Mick Bruno

ฉันคิดว่าการโอเวอร์โหลดครั้งแรกไม่มีวงเล็บในตอนท้าย: "return (x == null? false: IsNumeric (x.GetType ()));"
glenn garson

8

แทนที่จะกลิ้งของคุณเองวิธีที่เชื่อถือได้มากที่สุดที่จะบอกได้ว่าชนิดในการสร้างขึ้นเป็นตัวเลขที่น่าจะเป็นที่จะอ้างอิงและโทรMicrosoft.VisualBasic Information.IsNumeric(object value)การใช้งานจะจัดการกรณีที่ละเอียดอ่อนจำนวนมากเช่นchar[]และสตริง HEX และ OCT


นี่ควรจะอยู่บนสุด!
ว์ฟาล

4

มีแนวคิดที่แตกต่างกันสามประการ:

  • หากต้องการตรวจสอบว่าเป็นตัวเลขหรือไม่ (เช่นค่าตัวเลข (โดยทั่วไปมีกล่อง)) ให้ตรวจสอบประเภทด้วยis - ตัวอย่างเช่นif(obj is int) {...}
  • เพื่อตรวจสอบว่าสตริงสามารถแยกวิเคราะห์เป็นตัวเลขได้หรือไม่ ใช้TryParse()
  • แต่ถ้าวัตถุไม่ใช่ตัวเลขหรือสตริง แต่คุณสงสัยว่าToString()อาจให้บางอย่างที่ดูตัวเลขแล้วเรียก ToString()และถือว่าเป็นสตริง

ทั้งสองกรณีแรกคุณอาจต้องจัดการแยกประเภทตัวเลขแต่ละประเภทที่คุณต้องการสนับสนุน (double / decimal/ int) - แต่ละประเภทมีช่วงและความแม่นยำที่แตกต่างกันตัวอย่างเช่น

คุณยังสามารถดู regex เพื่อตรวจสอบคร่าวๆได้


4

สมมติว่าอินพุตของคุณเป็นสตริง ...

มี 2 ​​วิธีดังนี้

ใช้ Double.TryParse ()

double temp;
bool isNumber = Double.TryParse(input, out temp);

ใช้ Regex

 bool isNumber = Regex.IsMatch(input,@"-?\d+(\.\d+)?");

4

คุณสามารถใช้รหัสดังนี้:

if (n is IConvertible)
  return ((IConvertible) n).ToDouble(CultureInfo.CurrentCulture);
else
  // Cannot be converted.

ถ้าวัตถุของคุณเป็นInt32, Single, Doubleฯลฯ ก็จะทำการแปลง นอกจากนี้สตริงยังใช้IConvertibleแต่ถ้าสตริงไม่สามารถแปลงเป็นคู่ได้FormatExceptionจะถูกโยน


สตริงจริงจะถูกแยกวิเคราะห์ แต่ถ้าไม่อยู่ในรูปแบบที่ถูกต้อง FormatException จะถูกโยนทิ้ง
alfoks

@alfoks: คุณพูดถูกแล้วดังนั้นฉันจึงอัปเดตคำตอบ
Martin Liversage

1

หากความต้องการของคุณจริงๆ

.oString () จะส่งผลให้สตริงที่มีตัวเลขและ +, -,

และคุณต้องการใช้ double.TryParse จากนั้นคุณต้องใช้โอเวอร์โหลดที่รับพารามิเตอร์ NumberStyles และตรวจสอบให้แน่ใจว่าคุณใช้วัฒนธรรมที่ไม่แปรเปลี่ยน

ตัวอย่างเช่นสำหรับตัวเลขที่อาจมีเครื่องหมายนำหน้าไม่มีช่องว่างนำหน้าหรือต่อท้ายไม่มีตัวคั่นหลักพันและตัวคั่นทศนิยมของช่วงเวลาให้ใช้:

NumberStyles style = 
   NumberStyles.AllowLeadingSign | 
   NumberStyles.AllowDecimalPoint | 
double.TryParse(input, style, CultureInfo.InvariantCulture, out result);

1

ในขณะที่เขียนobject.IsNumeric()วิธีการขยายของฉันเองตามคำตอบของ Saul Dolgin สำหรับคำถามนี้ฉันพบปัญหาที่อาจเกิดขึ้นซึ่งคุณจะได้รับOverflowExceptionถ้าคุณลองใช้ด้วยdouble.MaxValueหรือdouble.MinValueหรือ

"วิธีแก้ปัญหา" ของฉันคือการรวมคำตอบที่ยอมรับจาก Noldorin กับคำตอบจาก Saul Dolgin และเพิ่มสวิตช์การจับคู่รูปแบบก่อนที่จะพยายามแยกวิเคราะห์สิ่งใด ๆ (และใช้ความดี C # 7 เพื่อทำให้เป็นระเบียบเล็กน้อย):

public static bool IsNumeric(this object obj)
{
    if (obj == null) return false;

    switch (obj)
    {
        case sbyte _: return true;
        case byte _: return true;
        case short _: return true;
        case ushort _: return true;
        case int _: return true;
        case uint _: return true;
        case long _: return true;
        case ulong _: return true;
        case float _: return true;
        case double _: return true;
        case decimal _: return true;
    }

    string s = Convert.ToString(obj, CultureInfo.InvariantCulture);

    return double.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out double _);
}

ระวังประเภทที่เป็นโมฆะ
Guillermo Prandi

0

ใช่มันใช้งานได้:

object x = 1;
Assert.That(x is int);

สำหรับตัวเลขทศนิยมคุณจะต้องทดสอบโดยใช้ประเภทลอย:

object x = 1f;
Assert.That(x is float);

สิ่งนี้จะใช้ได้ผลถ้าวัตถุนั้นเป็น int ก่อนที่จะส่งไปยังวัตถุโดยปริยายหรือโดยชัดแจ้ง ในตัวอย่างของคุณเลขวิเศษ 1 คือ int และจากนั้นจะโยนความไม่สมบูรณ์ลงในประเภทของตัวแปร x .. ถ้าคุณทำ object x = 1.0 คำยืนยันของคุณจะส่งกลับเท็จ
Dexter

มีตัวเลขที่ไม่ใช่ ints
Fredrik Mörk

ใช่ประเด็นของฉันก็คือสิ่งที่ @Noldorin มีในคำตอบของเขาตอนนี้
Peter Lillevold
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.