Kiểm tra xem một đối tượng có phải là một số trong C # không


90

Tôi muốn kiểm tra xem một đối tượng là một số do đó .ToString()sẽ cho kết quả trong một chuỗi có chứa chữ số và +, -,.

Có thể thực hiện bằng cách đơn giản kiểm tra .net (như if (p is Number):) không?

Hoặc Tôi có nên chuyển đổi thành chuỗi, sau đó thử phân tích cú pháp thành gấp đôi?

Cập nhật: Để làm rõ đối tượng của tôi là int, uint, float, double, v.v. nó không phải là một chuỗi. Tôi đang cố tạo một hàm tuần tự hóa bất kỳ đối tượng nào thành xml như sau:

<string>content</string>

hoặc là

<numeric>123.3</numeric>

hoặc nêu ra một ngoại lệ.


5
Có vẻ như bạn đang cố gắng viết XmlSerializer của riêng mình- có vấn đề gì với một nhà cung cấp của .NET- msdn.microsoft.com/en-us/library/… ?
RichardOD

2
Bạn có thể giải quyết toàn bộ vấn đề này bằng cách xác định định dạng XML của mình bằng XSD, sau đó tạo một đối tượng mà bạn có thể tuần tự hóa dữ liệu của mình bằng công cụ XSD được cung cấp - msdn.microsoft.com/en-us/library/x6c1kb0s % 28VS.71% 29.aspx
Dexter

@RichardOD: Tôi có thể sử dụng tuần tự hóa xml để tuần tự hóa đối tượng [] không? Tôi cần nó để gọi Flash chức năng adobe.com/livedocs/flex/201/html/wwhelp/wwhimpl/common/html/...
Piotr Czapla

Câu trả lời:


183

Bạn chỉ cần thực hiện kiểm tra kiểu cho từng kiểu số cơ bản.

Đây là một phương thức mở rộng sẽ thực hiện công việc:

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

Điều này sẽ bao gồm tất cả các loại số.

Cập nhật

Có vẻ như bạn thực sự muốn phân tích cú pháp số từ một chuỗi trong quá trình giải mã. Trong trường hợp này, nó có lẽ sẽ là tốt nhất để sử dụng double.TryParse.

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

Tất nhiên, điều này sẽ không xử lý các số nguyên rất lớn / số thập phân dài, nhưng nếu trường hợp đó xảy ra, bạn chỉ cần thêm các lệnh gọi bổ sung vào long.TryParse/ decimal.TryParse/ bất kỳ thứ gì khác.


Mục đích của tôi là int, short, uint, float, double hoặc bất cứ điều gì khác mà là một con số
Piotr Czapla

@Piotr: À đúng rồi. Có vẻ như tôi đã hiểu lầm bạn. Xem câu trả lời cập nhật của tôi.
Noldorin

1
@Noldorin: thực sự thì phiên bản mã trước đó của bạn cũng sẽ hoạt động; chỉ cần thêm một kiểm tra null và sử dụng giá trị.ToString (). Sau đó, bạn không cần phải kiểm tra tất cả các loại số.
Fredrik Mörk,

1
@Noldorin Thật đáng buồn là nó là giải pháp đúng là dài dòng :(.
Piotr Czapla

1
@Joe: Trên thực tế, nó sẽ không tạo ra sự khác biệt, vì ToString cũng sẽ sử dụng văn hóa hiện tại.
Noldorin

36

Lấy từ Blog của 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
Vấn đề với cách tiếp cận này là nếu bạn truyền vào một chuỗi trông giống như một số, nó sẽ định dạng nó. Có thể ổn đối với hầu hết mọi người nhưng đối với tôi đó là một điểm dừng chân.
Rob Sedgwick lúc

1
Một vấn đề tiềm ẩn khác với điều này là bạn không thể phân tích cú pháp các giá trị min / max cho double. double.Parse(double.MaxValue.ToString())nguyên nhân an OverflowException. Bạn có thể khắc phục điều này bằng cách cung cấp công cụ sửa đổi vòng .ToString("R")trong trường hợp này, nhưng quá tải đó không khả dụng Convert.ToString(...)vì chúng tôi không biết loại. Tôi biết đây là một trường hợp hơi rắc rối, nhưng tôi đã vấp phải nó khi viết các bài kiểm tra cho .IsNumeric()phần mở rộng của riêng mình . "Giải pháp" của tôi là thêm swtich kiểm tra kiểu trước khi cố gắng phân tích cú pháp bất kỳ thứ gì, hãy xem câu trả lời của tôi cho câu hỏi này để biết mã.
Ben

21

Tận dụng thuộc tính IsPrimitive để tạo một phương thức mở rộng hữu ích:

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

EDIT: Đã sửa chữa theo nhận xét. Các chỉ số chung đã bị xóa vì các loại giá trị hộp .GetType (). Cũng bao gồm sửa chữa cho các giá trị nullable.


1
Phần generics không cung cấp thêm cho bạn ở đây, phải không? bạn chỉ GetType truy cập () trong đó có sẵn trên đối tượng ...
Peter Lillevold

Nó tiết kiệm một hoạt động hộp nếu được gọi trên một loại giá trị. Suy nghĩ về khả năng tái sử dụng.
Kenan EK,

1
Tại sao không sử dụng typeof (T) thay vì obj.GetType, theo cách đó bạn sẽ không nhận được NullReferenceException nếu ai đó chuyển kiểu tham chiếu null. Bạn cũng có thể đặt một ràng buộc chung cho T để chỉ chấp nhận các loại giá trị. Tất nhiên bạn bắt đầu có rất nhiều thông tin tại thời điểm biên dịch nếu bạn làm điều đó.
Trillian

objectstringkhông phải là loại nguyên thủy.
We Are All Monica

@jnylen: câu trả lời này cách đây khá lâu. Tôi tin rằng tôi đã đào được phần nào đó từ nguồn khuôn khổ được phản chiếu vào thời điểm đó, nhưng ai có thể biết hôm nay ... Câu trả lời đã sửa.
Kenan EK

10

Có một số câu trả lời tuyệt vời ở trên. Đây là một giải pháp tất cả trong một. Ba quá tải cho các trường hợp khác nhau.

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

hãy xem xét thêm kiểm tra null
wiero

Không thực sự cần kiểm tra null - là một phương thức mở rộng, bạn không thể gọi nó với giá trị null. Tất nhiên ai đó vẫn có thể gọi như một hàm bình thường nhưng đó không phải là cách sử dụng mong đợi của một phương thức mở rộng.
Mick Bruno,

5
Tôi nghĩ người ta có thể gọi nó với giá trị null. đối tượng obj = null; obj.IsNumeric ();
wiero

Cảm ơn Weiro, đã sửa nó. Không nhận ra rằng có thể gọi phương thức mở rộng với giá trị null nhưng tất nhiên là như vậy!
Mick Bruno

Tôi nghĩ rằng tình trạng quá tải đầu tiên thiếu ngoặc ở cuối: "trở lại (x == null giả: IsNumeric (x.GetType ()));"
glenn Garson

8

Thay vì tự làm theo cách của riêng bạn, cách đáng tin cậy nhất để biết một kiểu tích hợp có phải là số hay không có lẽ là tham chiếu Microsoft.VisualBasicvà gọi Information.IsNumeric(object value). Việc triển khai xử lý một số trường hợp char[]phức tạp như chuỗi HEX và OCT.


Điều này phải ở trên cùng!
nawfal

4

Có ba khái niệm khác nhau ở đó:

  • để kiểm tra xem nó có phải một số hay không (tức là chính một giá trị số (thường được đóng hộp)), hãy kiểm tra loại với is- ví dụif(obj is int) {...}
  • để kiểm tra xem một chuỗi có thể được phân tích cú pháp thành một số hay không; sử dụngTryParse()
  • nhưng nếu đối tượng không phải là một số hoặc một chuỗi, nhưng bạn nghi ngờ ToString()có thể cung cấp một cái gì đó giống như một số, thì hãy gọi ToString() và coi nó như một chuỗi

Trong cả hai trường hợp đầu tiên, có thể bạn sẽ phải xử lý riêng từng loại số mà bạn muốn hỗ trợ ( double/ decimal/ int) - ví dụ: mỗi loại có phạm vi và độ chính xác khác nhau.

Bạn cũng có thể nhìn vào regex để kiểm tra sơ bộ nhanh chóng.


4

Giả sử đầu vào của bạn là một chuỗi ...

Có 2 cách:

sử dụng Double.TryParse ()

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

sử dụng Regex

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

4

Bạn có thể sử dụng mã như thế này:

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

Nếu đối tượng của bạn là một Int32, Single, Doublevv nó sẽ thực hiện việc chuyển đổi. Ngoài ra, một chuỗi thực hiện IConvertiblenhưng nếu chuỗi không thể chuyển đổi thành một chuỗi kép thì a FormatExceptionsẽ được ném ra.


Trên thực tế, các chuỗi sẽ được phân tích cú pháp, nhưng nếu không ở định dạng chính xác, FormatException sẽ được ném ra.
alfoks

@alfoks: Bạn hoàn toàn đúng nên tôi đã cập nhật câu trả lời.
Martin Liversage

1

Nếu yêu cầu của bạn thực sự là

.ToString () sẽ dẫn đến một chuỗi chứa các chữ số và +, -,.

và bạn muốn sử dụng double.TryParse thì bạn cần sử dụng quá tải có tham số NumberStyles và đảm bảo rằng bạn đang sử dụng văn hóa bất biến.

Ví dụ: đối với một số có thể có dấu ở đầu, không có khoảng trắng ở đầu hoặc ở cuối, không có dấu phân cách hàng nghìn và dấu phân cách thập phân dấu chấm, hãy sử dụng:

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

1

Trong khi viết object.IsNumeric()phương pháp mở rộng của riêng tôi dựa trên câu trả lời của Saul Dolgin cho câu hỏi này, tôi đã gặp phải một vấn đề tiềm ẩn mà bạn sẽ nhận được OverflowExceptionnếu bạn thử nó với double.MaxValuehoặc double.MinValue.

"Giải pháp" của tôi là kết hợp câu trả lời được chấp nhận từ Noldorin với câu trả lời từ Saul Dolgin và thêm một công tắc khớp mẫu trước khi cố gắng phân tích cú pháp bất kỳ thứ gì (và sử dụng một số tính năng tốt của C # 7 để thu gọn một chút):

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

Hãy cẩn thận với các kiểu vô hiệu.
Guillermo Prandi

0

Có, điều này hoạt động:

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

Đối với một số dấu phẩy động, bạn sẽ phải kiểm tra bằng kiểu float:

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

Điều này sẽ hoạt động nếu đối tượng là một int trước khi được truyền ngầm hoặc rõ ràng cho một đối tượng. Trong ví dụ của bạn, số ma thuật 1 là một số int, và sau đó nó được chuyển thành kiểu của biến x .. Nếu bạn thực hiện đối tượng x = 1.0, thì xác nhận của bạn sẽ trả về sai.
Dexter

Có những số không phải là int.
Fredrik Mörk,

vâng, vì vậy quan điểm của tôi về cơ bản là những gì @Noldorin có trong câu trả lời của anh ấy ngay bây giờ.
Peter Lillevold,
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.