首页 > 学院 > 开发设计 > 正文

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

2019-11-17 02:47:59
字体:
来源:转载
供稿:网友
.Net判断一个对象是否为数值类型探讨总结(高营养含量,含最终代码及跑分)

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

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

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

探讨贡献提名典礼

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

image

image

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

image

image

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

image

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

image

其存在的问题主要是 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()[0].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 = 0; i < 500000; i++)            {                Assert.IsTrue((591).GetType().IsNumericType());                Assert.IsTrue((31.131).GetType().IsNumericType());                Assert.IsTrue((31.131f).GetType().IsNumericType());                Assert.IsTrue(((Int64)31).GetType().IsNumericType());                Assert.IsTrue((new decimal(31.351)).GetType().IsNumericType());                Assert.IsTrue((new Decimal(31.351)).GetType().IsNumericType());                Assert.IsTrue(((byte)31).GetType().IsNumericType());                Assert.IsTrue(((UInt64)31).GetType().IsNumericType());                Assert.IsTrue(((UIntPtr)31).GetType().IsNumericType());                Assert.IsTrue(((short)31).GetType().IsNumericType());                Assert.IsTrue(((Single)31).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(2).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<> 类型,可空类型的判断就是给这种情况使用的。

老外!不服跑个分?

6b1394a6gw1ejy5mn1n6wj20ga095mxk

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

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

我们的代码测试结果:

image

可以看出这个效率还是蛮高的,平均每轮耗时: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);

老外的代码测试结果:

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表