.Net判断一个对象是否为数值类型探讨总结(高营养含量,含最终代码及跑分)...
前一篇發(fā)出來后引發(fā)了積極的探討,起到了拋磚引玉效果,感謝大家參與。
吐槽一下:這個(gè)問題比其看起來要難得多得多啊。
大家的討論最終還是沒有一個(gè)完全正確的答案,不過我根據(jù)討論結(jié)果總結(jié)了一個(gè)差不多算是最終版的代碼,這里分享出來,畢竟這是大家共同的智慧結(jié)晶,沒有交流和碰撞就沒有這段代碼。
?
探討貢獻(xiàn)提名典禮
首先感謝 花生!~~ 以及 NETRUBE 提出了使用 GetTypeCode() 獲取類型代碼的方式,這個(gè)比 typeof() 的性能要高,但是有一點(diǎn)局限性,后面代碼中會(huì)指出。
由 JTANS 以及 入夏 提出的 ValueType 判斷也是有意義的,但顯然僅僅做這個(gè)判斷只能確定是否為值類型,還不能確定是否為我們要的數(shù)值類型。
由 石山不高 提出 Decimal 是非基元類型,這是正確的,我們?cè)谧罱K代碼中對(duì)其進(jìn)行了特殊處理。
由 花生 (為什么有兩個(gè)叫花生的!(+﹏+)~)給出的代碼比較完善,是比較具有總結(jié)性的討論成果了,最接近最終版:
其存在的問題主要是 char 和 bool 類型還是會(huì)被當(dāng)做數(shù)值,以及判斷順序需要小幅優(yōu)化。
?
(可能也許大概差不離就是)最終版代碼(也可能不是)
除了對(duì)上述存在問題的改進(jìn),還重新調(diào)整為3個(gè)方法,分別是用來判斷是否為數(shù)值類型、可空數(shù)值類型及可空類型。
/// <summary>/// 判斷是否為數(shù)值類型。/// </summary>/// <param name="t">要判斷的類型</param>/// <returns>是否為數(shù)值類型</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>/// 判斷是否為可空數(shù)值類型。/// </summary>/// <param name="t">要判斷的類型</param>/// <returns>是否為可空數(shù)值類型</returns>public static bool IsNumericOrNullableNumericType(this Type t){return t.IsNumericType() || (t.IsNullableType() && t.GetGenericArguments()[0].IsNumericType());}/// <summary>/// 判斷是否為可空類型。/// 注意,直接調(diào)用可空對(duì)象的.GetType()方法返回的會(huì)是其泛型值的實(shí)際類型,用其進(jìn)行此判斷肯定返回false。/// </summary>/// <param name="t">要判斷的類型</param>/// <returns>是否為可空類型</returns>public static bool IsNullableType(this Type t){return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>);}?
為了累死電腦而設(shè)計(jì)的測(cè)試用代碼
使用這個(gè)測(cè)試代碼跑可以通過,基本涵蓋了常用類型。
[TestClass]public class BasicTest{[TestMethod]public void 數(shù)值類型判斷測(cè)試(){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());}}}需指出的是:
這里對(duì)可空類型判斷沒有使用 GetType() 方法獲取類型對(duì)象,因?yàn)槲覝y(cè)試了一下,可空類型執(zhí)行 GetType() 返回的仍然是不可空的原類型,直接進(jìn)行判斷是否為數(shù)值類型即可。
那么為什么還要做針對(duì)可空類型的判斷呢?如果你試過在 ASP.Net Mvc 中獲取到模型屬性的 ModelMetadata 你就會(huì)知道,其 ModelType 屬性返回的就是 Nullable<> 類型,可空類型的判斷就是給這種情況使用的。
?
老外!不服跑個(gè)分?
JEFFERY YOU 提出應(yīng)該做一個(gè)測(cè)試,確實(shí)數(shù)據(jù)最有說服力。
我們就以上面的測(cè)試代碼來跑,注意這是循環(huán)五十萬輪的測(cè)試,每輪執(zhí)行該方法36次,共計(jì)執(zhí)行一千八百萬次,我們讓代碼連續(xù)跑三遍,取第三遍的時(shí)間結(jié)果(第一遍的包含初始化流程,肯定會(huì)慢一些)。
我們的代碼測(cè)試結(jié)果:
可以看出這個(gè)效率還是蠻高的,平均每輪耗時(shí):0.016546毫秒,平均每次執(zhí)行方法耗時(shí):0.0004596111111毫秒
然后我們把老外的代碼拿過來看一下,它跑不通這個(gè)測(cè)試,因?yàn)橐韵骂愋退鼪]做判斷:Decimal、Byte、UIntPtr 。
還有個(gè)我們測(cè)試代碼之外的 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è)試結(jié)果:
這是妥妥的輸給我們了,老外給咱跪了,那些支持簡單粗暴實(shí)打?qū)嵉呐笥彦e(cuò)了。
但是稍等一下,老外的代碼里其實(shí)有些明顯的重復(fù)判斷,比如在C#中 typeof() 獲取的 int 和 Int32 其實(shí)是一樣的,我們來優(yōu)化一下這些重復(fù):
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);優(yōu)化版的老外代碼測(cè)試結(jié)果:
哈,老外還是跪給我們了。
下面我們?cè)賹⑦@個(gè)代碼改進(jìn)為使用 TypeCode 方式進(jìn)行判斷,這會(huì)提高一些性能。
但是需要注意:
從 Enum 類型中獲取到的 TypeCode 會(huì)是對(duì)應(yīng) Int32 類型,這不是我們要的結(jié)果,需要額外對(duì)其進(jìn)行判斷。
TypeCode 枚舉中是沒有? IntPtr 和 UIntPtr 項(xiàng)的,所以還是要做額外判斷。
改進(jìn)后的代碼:
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);}老外的代碼改進(jìn)為用 TypeCode 方式進(jìn)行判斷后的測(cè)試結(jié)果:
這個(gè)效果就很不錯(cuò)了,一千八百萬次的量級(jí),僅僅是比我們的最終代碼慢了81毫秒(實(shí)測(cè)三遍時(shí)是穩(wěn)定地輸給我們的代碼,不是飄出來的偶然浮動(dòng)結(jié)果),這個(gè)性能差距可以忽略了。
這也可以看做是另一個(gè)最終版的代碼了,因?yàn)槿绻鶕?jù)你的使用環(huán)境來把常用類型放到最前面的話,性能還會(huì)更好(盡管你根本感覺不到單次萬分之幾毫秒的差別),但是不可回避的是對(duì)那些我們沒有預(yù)見到的類型的支持問題,比如這? IntPtr 和 UIntPtr ,在我們前面給出的最終版代碼中這兩個(gè)類型是未做特殊適配就天然支持的。
所以如果你重視優(yōu)雅度、擴(kuò)展性和編碼知識(shí)層級(jí)的話,還是建議你使用我前面給出的最終代碼。
?
巡回總結(jié)報(bào)告會(huì)演講
看似非常簡單的問題,背后卻有這么深的水啊,若沒有大家的討論,斷然不會(huì)得到這樣的成果,并且學(xué)到這么多知識(shí)。
沒有完美的代碼,我們期待更好,在此繼續(xù)討論吧,也許交流碰撞后還會(huì)有更優(yōu)秀的方案!
(微軟:臥槽,看你們這么苦逼,我給你們直接做一個(gè)屬性出來吧,請(qǐng)期待.Net 框架v10.29博主生日特別無碼漢化激情未刪減導(dǎo)演剪輯泄露藍(lán)光3D版………嗯,我們將其委托給暴雪工作室開發(fā)。)
轉(zhuǎn)載于:https://www.cnblogs.com/SkyD/p/4058486.html
總結(jié)
以上是生活随笔為你收集整理的.Net判断一个对象是否为数值类型探讨总结(高营养含量,含最终代码及跑分)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL调用C# dll(第一中DLL,没
- 下一篇: nginx启动与停止