前一篇发出来后引发了积极的探讨,起到了抛砖引玉效果,感谢大家参与。

吐槽一下:这个问题比其看起来要难得多得多啊。

大家的讨论最终还是没有一个完全正确的答案,不过我根据讨论结果总结了一个差不多算是最终版的代码,这里分享出来,毕竟这是大家共同的智慧结晶,没有交流和碰撞就没有这段代码。

探讨贡献提名典礼

首先感谢 花生!~~ 以及 NETRUBE 提出了使用 GetTypeCode() 获取类型代码的方式,这个比 typeof() 的性能要高,但是有一点局限性,后面代码中会指出。

JTANS 以及 入夏 提出的 ValueType 判断也是有意义的,但显然仅仅做这个判断只能确定是否为值类型,还不能确定是否为我们要的数值类型。

石山不高 提出 Decimal 是非基元类型,这是正确的,我们在最终代码中对其进行了特殊处理。

花生 (为什么有两个叫花生的!(+﹏+)~)给出的代码比较完善,是比较具有总结性的讨论成果了,最接近最终版:

其存在的问题主要是 char 和 bool 类型还是会被当做数值,以及判断顺序需要小幅优化。

(可能也许大概差不离就是)最终版代码(也可能不是)

除了对上述存在问题的改进,还重新调整为3个方法,分别是用来判断是否为数值类型、可空数值类型及可空类型。

/// <summary>
/// 判断是否为数值类型。
/// </summary>
/// <param name="t">要判断的类型</param>
/// <returns>是否为数值类型</returns>
public static bool IsNumericType(this Type t)
{
var tc = Type.GetTypeCode(t);
return (t.IsPrimitive && t.IsValueType && !t.IsEnum && tc != TypeCode.Char && tc != TypeCode.Boolean) || tc == TypeCode.Decimal;
} /// <summary>
/// 判断是否为可空数值类型。
/// </summary>
/// <param name="t">要判断的类型</param>
/// <returns>是否为可空数值类型</returns>
public static bool IsNumericOrNullableNumericType(this Type t)
{
return t.IsNumericType() || (t.IsNullableType() && t.GetGenericArguments()[].IsNumericType());
} /// <summary>
/// 判断是否为可空类型。
/// 注意,直接调用可空对象的.GetType()方法返回的会是其泛型值的实际类型,用其进行此判断肯定返回false。
/// </summary>
/// <param name="t">要判断的类型</param>
/// <returns>是否为可空类型</returns>
public static bool IsNullableType(this Type t)
{
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}

为了累死电脑而设计的测试用代码

使用这个测试代码跑可以通过,基本涵盖了常用类型。

[TestClass]
public class BasicTest
{
[TestMethod]
public void 数值类型判断测试()
{
for (int i = ; i < ; i++)
{
Assert.IsTrue(().GetType().IsNumericType());
Assert.IsTrue((31.131).GetType().IsNumericType());
Assert.IsTrue((31.131f).GetType().IsNumericType());
Assert.IsTrue(((Int64)).GetType().IsNumericType());
Assert.IsTrue((new decimal(31.351)).GetType().IsNumericType());
Assert.IsTrue((new Decimal(31.351)).GetType().IsNumericType());
Assert.IsTrue(((byte)).GetType().IsNumericType());
Assert.IsTrue(((UInt64)).GetType().IsNumericType());
Assert.IsTrue(((UIntPtr)).GetType().IsNumericType());
Assert.IsTrue(((short)).GetType().IsNumericType());
Assert.IsTrue(((Single)).GetType().IsNumericType()); Assert.IsTrue((typeof(Int64?)).IsNumericOrNullableNumericType());
Assert.IsTrue((typeof(UInt64?)).IsNumericOrNullableNumericType());
Assert.IsTrue((typeof(decimal?)).IsNumericOrNullableNumericType());
Assert.IsTrue((typeof(Decimal?)).IsNumericOrNullableNumericType());
Assert.IsTrue((typeof(UIntPtr?)).IsNumericOrNullableNumericType());
Assert.IsTrue((typeof(byte?)).IsNumericOrNullableNumericType());
Assert.IsTrue((typeof(Single?)).IsNumericOrNullableNumericType());
Assert.IsTrue((typeof(Double?)).IsNumericOrNullableNumericType());
Assert.IsTrue((typeof(float?)).IsNumericOrNullableNumericType());
Assert.IsTrue((typeof(double?)).IsNumericOrNullableNumericType());
Assert.IsTrue((typeof(int?)).IsNumericOrNullableNumericType());
Assert.IsTrue((typeof(short?)).IsNumericOrNullableNumericType());
Assert.IsTrue((typeof(Nullable<Byte>)).IsNumericOrNullableNumericType()); Assert.IsFalse(DateTime.Now.GetType().IsNumericType());
Assert.IsFalse(TimeSpan.FromDays().GetType().IsNumericType());
Assert.IsFalse("aacc".GetType().IsNumericType());
Assert.IsFalse(System.UriPartial.Path.GetType().IsNumericType());
Assert.IsFalse('c'.GetType().IsNumericType());
Assert.IsFalse(false.GetType().IsNumericType()); Assert.IsFalse((typeof(DateTime?)).IsNumericOrNullableNumericType());
Assert.IsFalse((typeof(Char?)).IsNumericOrNullableNumericType());
Assert.IsFalse((typeof(char?)).IsNumericOrNullableNumericType());
Assert.IsFalse((typeof(System.UriPartial?)).IsNumericOrNullableNumericType());
Assert.IsFalse((typeof(Boolean?)).IsNumericOrNullableNumericType());
Assert.IsFalse((typeof(bool?)).IsNumericOrNullableNumericType());
} }
}

需指出的是:

这里对可空类型判断没有使用 GetType() 方法获取类型对象,因为我测试了一下,可空类型执行 GetType() 返回的仍然是不可空的原类型,直接进行判断是否为数值类型即可。

那么为什么还要做针对可空类型的判断呢?如果你试过在 ASP.Net Mvc 中获取到模型属性的 ModelMetadata 你就会知道,其 ModelType 属性返回的就是 Nullable<> 类型,可空类型的判断就是给这种情况使用的。

老外!不服跑个分?

JEFFERY YOU 提出应该做一个测试,确实数据最有说服力。

我们就以上面的测试代码来跑,注意这是循环五十万轮的测试,每轮执行该方法36次,共计执行一千八百万次,我们让代码连续跑三遍,取第三遍的时间结果(第一遍的包含初始化流程,肯定会慢一些)。

我们的代码测试结果:

可以看出这个效率还是蛮高的,平均每轮耗时:0.016546毫秒,平均每次执行方法耗时:0.0004596111111毫秒

然后我们把老外的代码拿过来看一下,它跑不通这个测试,因为以下类型它没做判断:Decimal、Byte、UIntPtr 。

还有个我们测试代码之外的 IntPtr 。

加上这些类型的判断之后,主体方法代码如下:

return t == typeof(int)
|| t == typeof(double)
|| t == typeof(long)
|| t == typeof(short)
|| t == typeof(float)
|| t == typeof(Int16)
|| t == typeof(Int32)
|| t == typeof(Int64)
|| t == typeof(uint)
|| t == typeof(UInt16)
|| t == typeof(UInt32)
|| t == typeof(UInt64)
|| t == typeof(sbyte)
|| t == typeof(Single)
|| t == typeof(Decimal)
|| t == typeof(Byte)
|| t == typeof(UIntPtr)
|| t == typeof(IntPtr);

老外的代码测试结果:

这是妥妥的输给我们了,老外给咱跪了,那些支持简单粗暴实打实的朋友错了。

但是稍等一下,老外的代码里其实有些明显的重复判断,比如在C#中 typeof() 获取的 int 和 Int32 其实是一样的,我们来优化一下这些重复:

return t == typeof(Int16)
|| t == typeof(Int32)
|| t == typeof(Int64)
|| t == typeof(Single)
|| t == typeof(Double)
|| t == typeof(UInt16)
|| t == typeof(UInt32)
|| t == typeof(UInt64)
|| t == typeof(Byte)
|| t == typeof(Decimal)
|| t == typeof(SByte)
|| t == typeof(UIntPtr)
|| t == typeof(IntPtr);

优化版的老外代码测试结果:

哈,老外还是跪给我们了。

下面我们再将这个代码改进为使用 TypeCode 方式进行判断,这会提高一些性能。

但是需要注意:

从 Enum 类型中获取到的 TypeCode 会是对应 Int32 类型,这不是我们要的结果,需要额外对其进行判断。

TypeCode 枚举中是没有  IntPtr 和 UIntPtr 项的,所以还是要做额外判断。

改进后的代码:

if (t.IsEnum) return false;
var tc = Type.GetTypeCode(t);
switch (tc)
{
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
case TypeCode.Byte:
case TypeCode.Decimal:
case TypeCode.SByte:
return true;
default:
return t == typeof(UIntPtr) || t == typeof(IntPtr);
}

老外的代码改进为用 TypeCode 方式进行判断后的测试结果:

这个效果就很不错了,一千八百万次的量级,仅仅是比我们的最终代码慢了81毫秒(实测三遍时是稳定地输给我们的代码,不是飘出来的偶然浮动结果),这个性能差距可以忽略了。

这也可以看做是另一个最终版的代码了,因为如果根据你的使用环境来把常用类型放到最前面的话,性能还会更好(尽管你根本感觉不到单次万分之几毫秒的差别),但是不可回避的是对那些我们没有预见到的类型的支持问题,比如这  IntPtr 和 UIntPtr ,在我们前面给出的最终版代码中这两个类型是未做特殊适配就天然支持的。

所以如果你重视优雅度、扩展性和编码知识层级的话,还是建议你使用我前面给出的最终代码。

巡回总结报告会演讲

看似非常简单的问题,背后却有这么深的水啊,若没有大家的讨论,断然不会得到这样的成果,并且学到这么多知识。

没有完美的代码,我们期待更好,在此继续讨论吧,也许交流碰撞后还会有更优秀的方案!

(微软:卧槽,看你们这么苦逼,我给你们直接做一个属性出来吧,请期待.Net 框架v10.29博主生日特别无码汉化激情未删减导演剪辑泄露蓝光3D版………嗯,我们将其委托给暴雪工作室开发。)

.Net判断一个对象是否为数值类型探讨总结(高营养含量,含最终代码及跑分)的更多相关文章

  1. .Net判断一个对象是否为数值类型

    这乍一听是个很简单的事,但突然搞起来还真有点无从下手的感觉. 首先当然是通过GetType()方法反射获取其类型信息,然后对其进行分析,但是类型信息Type中并没有简单地给出这么一个属性进行判断. 老 ...

  2. Net判断一个对象是否为数值类型 z

    http://www.cnblogs.com/SkyD/p/4053461.html public static bool IsNumeric(this Type dataType) { if (da ...

  3. 如何判断一个对象实例是不是某个类型,如Cat类型

    <script> function cat(){} var b = new cat(); if(b instanceof cat){ console.log("a是cat&quo ...

  4. js中判断一个对象的类型的种种方法

    javascript中检测对象的类型的运算符有:typeof.constructor.instanceof. typeof:typeof是一个一元运算符,返回结果是一个说明运算数类型的字符串.如:&q ...

  5. WPF 判断一个对象是否是设计时的窗口类型,而不是运行时的窗口

    原文:WPF 判断一个对象是否是设计时的窗口类型,而不是运行时的窗口 当我们对 Window 类型写一个附加属性的时候,在属性变更通知中我们需要判断依赖对象是否是一个窗口.但是,如果直接判断是否是 W ...

  6. js -【 数组】判断一个变量是数组类型的几种方法

    怎么判断一个数组是数组呢? 其实这个也是一个常考的题目.依稀记得我为数不多的面试经过中都被问道过. 方案一: instanceof variable instanceof Array 解决思路: 使用 ...

  7. MySQL的数值类型,时间

    数值类型 整数型 tinyint  smallint  mediumint  int|integer  bigint 注意: 1, 如何选择数据类型,我们的原则是:够用就行!尽量的选择占用内存小的整型 ...

  8. 005 Python的数值类型

    005 Python的数值类型 BIF    指的是内置函数,一般不作为变量命名.如 input,while,if,else,float,等等.整型:整数.(python3.0版本把整型和长整型结合在 ...

  9. js如何判断一个对象是不是Array?(转载)

    js如何判断一个对象是不是Array? 在开发中,我们经常需要判断某个对象是否为数组类型,在Js中检测对象类型的常见方法都有哪些呢? typeof 操作符 对于Function, String, Nu ...

随机推荐

  1. Bat命令学习

    基础部分:====================================================================== 一.基础语法: 1.批处理文件是一个“.bat” ...

  2. Word自动生成目录

    博主最近在写报告的时候要在Word里面做个目录,再做个页码,然后上网搜了一些方法,非常零散,我弄了好久才弄好.在这里我把整套方法分享一下. 声明:内容完全独创! 工具:Word 2016. 效果:如下 ...

  3. python中x,y交换值的问题

    今天碰到了python和其他语言不同的问题:赋值语句 x,y,z=1,2,3,执行 z,x,y=y,z,x 后,x.y.z 中分别含有什么值? 我想的是 x=2  y=2  z=2 可调试后应该是:x ...

  4. webstorm安装后的一些设置技巧:

    如何更改主题(字体&配色):File -> settings -> Editor -> colors&fonts -> scheme name.主题下载地址 如 ...

  5. Java web--Filter过滤器分IP统计访问次数

    分IP统计访问次数即网站统计每个IP地址访问本网站的次数. 分析 因为一个网站可能有多个页面,无论哪个页面被访问,都要统计访问次数,所以使用过滤器最为方便. 因为需要分IP统计,所以可以在过滤器中创建 ...

  6. java的继承和重写

    继承是面向对象最显著的一个特性.继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力.[1]  Java继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以 ...

  7. 38、重新复习javascript之三

    前言 虽然吧,每天都没有什么太有技术性的工作者,但是技术不能丢,希望也要有,人如果没有希望那不就和咸鱼一样了吗?小伙加油吧 1.html与javascript结合 <!DOCTYPE html ...

  8. python---常见的数据队列

    一.FIFO队列(先进先出) #!/usr/bin/env python # -*- coding:utf-8 -*- __author__ = 'zhoufeng' #FIFO队列 import q ...

  9. Linux如何查看进程、杀死进程、启动进程等常用命令

    Linux如何查看进程.杀死进程.启动进程等常用命令 关键字: linux 查进程.杀进程.起进程1.查进程    ps命令查找与进程相关的PID号:    ps a 显示现行终端机下的所有程序,包括 ...

  10. WCF中常用的binding方式

    WCF中常用的binding方式: BasicHttpBinding: 用于把 WCF 服务当作 ASMX Web 服务.用于兼容旧的Web ASMX 服务.WSHttpBinding: 比 Basi ...