前一篇發出來後引發了積極的探討,起到了拋磚引玉效果,感謝大家參與。
吐槽一下:這個問題比其看起來要難得多得多啊。
大家的討論最終還是沒有一個完全正確的答案,不過我根據討論結果總結了一個差不多算是最終版的代碼,這裡分享出來,畢竟這是大家共同的智慧結晶,沒有交流和碰撞就沒有這段代碼。
首先感謝 花生!~~ 以及 NETRUBE 提出了使用 GetTypeCode() 獲取類型代碼的方式,這個比 typeof() 的性能要高,但是有一點局限性,後面代碼中會指出。
(可能也許大概差不離就是)最終版代碼(也可能不是)
除了對上述存在問題的改進,還重新調整為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<> 類型,可空類型的判斷就是給這種情況使用的。
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);
老外的代碼測試結果:
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);
優化版的老外代碼測試結果:
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 方式進行判斷後的測試結果:
巡回總結報告會演講
看似非常簡單的問題,背後卻有這麼深的水啊,若沒有大家的討論,斷然不會得到這樣的成果,並且學到這麼多知識。
沒有完美的代碼,我們期待更好,在此繼續討論吧,也許交流碰撞後還會有更優秀的方案!
數據源可能是讀取自數據庫,也可能是文本文件、xml文件等,常見的有以下幾種:
Xml、DataSet、IEnumerable<T>。