【问题标题】:Checking if an object is a number in C#在 C# 中检查对象是否为数字
【发布时间】:2010-11-10 23:16:08
【问题描述】:

我想检查一个对象是否是一个数字,以便.ToString() 会生成一个包含数字和+,-,. 的字符串

是否可以通过 .net 中的简单类型检查(如:if (p is Number))?

或者我应该转换为字符串,然后尝试解析为双精度?

更新:为了澄清我的对象是 int、uint、float、double 等,它不是字符串。 我正在尝试创建一个将任何对象序列化为 xml 的函数,如下所示:

<string>content</string>

<numeric>123.3</numeric>

或引发异常。

【问题讨论】:

标签: c# .net serialization xml-serialization


【解决方案1】:

您只需对每种基本数字类型进行类型检查。

这是一个可以完成这项工作的扩展方法:

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

这应该涵盖所有数字类型。

更新

您似乎确实想在反序列化期间从字符串中解析数字。在这种情况下,最好使用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、short、uint、float、double 或其他任何数字
  • @Piotr:嗯,对。看来我误会了你。请参阅我的更新答案。
  • @Noldorin:实际上你以前版本的代码也可以工作;只需添加一个空检查并使用 value.ToString()。那么你就不需要检查所有的数字类型了。
  • @Noldorin 遗憾的是,正确的解决方案是冗长的:(。
  • @Joe:实际上,这不会有什么不同,因为 ToString 也会使用当前的文化。
【解决方案2】:

是的,这行得通:

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

对于浮点数,您必须使用浮点类型进行测试:

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

【讨论】:

  • 如果对象在被隐式或显式转换为对象之前是一个 int,这将起作用。在您的示例中,幻数 1 是一个 int,然后隐式转换为变量 x.. 的类型。如果您完成了 object x = 1.0,您的断言将返回 false。
  • 有些数字不是整数。
  • 是的,所以我的观点基本上就是@Noldorin 现在的回答。
【解决方案3】:

假设您的输入是一个字符串...

有两种方式:

使用 Double.TryParse()

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

使用正则表达式

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

【讨论】:

    【解决方案4】:

    你可以使用这样的代码:

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

    如果您的对象是Int32SingleDouble 等,它将执行转换。此外,字符串实现了IConvertible,但如果字符串不能转换为双精度,则会抛出FormatException

    【讨论】:

    • 实际上会解析字符串,但如果格式不正确,则会抛出 FormatException。
    • @alfoks:你说得对,所以我更新了答案。
    【解决方案5】:

    这里有三个不同的概念:

    • 要检查它是否数字(即(通常是装箱的)数值本身),请使用is检查类型 - 例如if(obj is int) {...}
    • 检查是否可以将字符串解析为数字;使用TryParse()
    • 但如果对象不是数字或字符串,但您怀疑ToString() 可能会给出看起来像数字的东西,那么调用 ToString()并将其视为字符串

    在前两种情况下,您可能必须分别处理要支持的每种数字类型 (double/decimal/int) - 例如,每种类型都有不同的范围和精度。

    您还可以查看正则表达式进行快速粗略检查。

    【讨论】:

      【解决方案6】:

      取自Scott Hanselman's Blog

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

      【讨论】:

      • 这种方法的问题是如果你传入一个看起来像数字的字符串,它会格式化它。对大多数人来说可能没问题,但对我来说这是一个表演障碍。
      • 另一个潜在问题是您无法解析双精度的最小/最大值。 double.Parse(double.MaxValue.ToString()) 导致 OverflowException。在这种情况下,您可以通过提供往返修饰符 .ToString("R") 来解决此问题,但该重载不适用于 Convert.ToString(...),因为我们不知道类型。我知道这是一个边缘案例,但我在为自己的 .IsNumeric() 扩展编写测试时偶然发现了它。我的“解决方案”是在尝试解析任何内容之前添加一个类型检查开关,请参阅我对这个问题的回答以获取代码。
      【解决方案7】:

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

      编辑:根据 cmets 修复。 由于 .GetType() 框值类型,泛型已被删除。还包括可空值的修复。

      【讨论】:

      • 泛型部分在这里没有给你任何额外的东西,是吗?您只能访问对象上可用的 GetType()...
      • 如果在值类型上调用,它会保存一个框操作。考虑可重用性。
      • 为什么不使用 typeof(T) 而不是 obj.GetType,这样如果有人传递了 null 引用类型,你就不会得到 NullReferenceException。您还可以对 T 施加通用约束以仅接受值类型。当然,如果你这样做,你会在编译时开始获得大量信息。
      • objectstring 不是原始类型。
      • @jnylen:这个答案是很久以前的了。我相信我当时从反射框架源中挖出了一些东西,但是今天谁能说出来......固定答案。
      【解决方案8】:

      如果你的要求是真的

      .ToString() 会产生一个字符串 包含数字和 +,-,.

      并且你想使用 double.TryParse 那么你需要使用带有 NumberStyles 参数的重载,并确保你使用的是不变的文化。

      例如,对于可能有前导符号、没有前导或尾随空格、没有千位分隔符和句点小数分隔符的数字,请使用:

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

      【讨论】:

        【解决方案9】:

        上面有一些很好的答案。这是一个多合一的解决方案。针对不同情况的三种重载。

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

        【讨论】:

        • 考虑添加空检查
        • 真的不需要空值检查——作为一种扩展方法,你不能用空值调用它。当然,仍然有人可以作为普通函数调用,但这不是扩展方法的预期用法。
        • 我认为可以用空值调用它。对象 obj = null; obj.IsNumeric();
        • 感谢 Weiro,已修复。没有意识到可以使用 null 值调用扩展方法,但当然可以!
        • 我认为第一个重载在末尾缺少一个括号:“return (x==null ? false : IsNumeric(x.GetType()));”
        【解决方案10】:

        判断内置类型是否为数字的最可靠方法可能是引用Microsoft.VisualBasic 并调用Information.IsNumeric(object value),而不是自己滚动。该实现处理了许多微妙的情况,例如 char[] 以及 HEX 和 OCT 字符串。

        【讨论】:

        • 这应该在顶部!
        【解决方案11】:

        在根据 Saul Dolgin 对这个问题的回答编写我自己的 object.IsNumeric() 扩展方法时,我遇到了一个潜在的问题,即如果您尝试使用 double.MaxValuedouble.MinValue,您将得到一个 OverflowException

        我的“解决方案”是将 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 _);
        }
        

        【讨论】:

        • 当心可空类型。
        猜你喜欢
        • 2015-04-05
        • 1970-01-01
        • 1970-01-01
        • 2021-03-20
        • 1970-01-01
        • 2010-12-23
        • 1970-01-01
        • 2021-07-17
        • 1970-01-01
        相关资源
        最近更新 更多