Type 類提供了大量的屬性和方法,但在一些基礎性開發工作中,Type類功能還有些欠缺,尤其上在處理泛型類型時,如可空類型和泛型集合類型。下面的類就針對這些地方進行擴展。
1 public static class TypeHelper
2 {
3 public static bool IsNullableType(this Type type)
4 {
5 return (((type != null) && type.IsGenericType) &&
6 (type.GetGenericTypeDefinition() == typeof(Nullable<>)));
7 }
8
9 public static Type GetNonNullableType(this Type type)
10 {
11 if (IsNullableType(type))
12 {
13 return type.GetGenericArguments()[0];
14 }
15 return type;
16 }
17
18 public static bool IsEnumerableType(this Type enumerableType)
19 {
20 return (FindGenericType(typeof(IEnumerable<>), enumerableType) != null);
21 }
22
23 public static Type GetElementType(this Type enumerableType)
24 {
25 Type type = FindGenericType(typeof(IEnumerable<>), enumerableType);
26 if (type != null)
27 {
28 return type.GetGenericArguments()[0];
29 }
30 return enumerableType;
31 }
32
33 public static bool IsKindOfGeneric(this Type type, Type definition)
34 {
35 return (FindGenericType(definition, type) != null);
36 }
37
38 public static Type FindGenericType(this Type definition, Type type)
39 {
40 while ((type != null) && (type != typeof(object)))
41 {
42 if (type.IsGenericType && (type.GetGenericTypeDefinition() == definition))
43 {
44 return type;
45 }
46 if (definition.IsInterface)
47 {
48 foreach (Type type2 in type.GetInterfaces())
49 {
50 Type type3 = FindGenericType(definition, type2);
51 if (type3 != null)
52 {
53 return type3;
54 }
55 }
56 }
57 type = type.BaseType;
58 }
59 return null;
60 }
61 }
從名字上就以大體知道方法的功能,下面是部分測試代碼,幫助大家理解:
1 [TestMethod()]
2 public void IsNullableTypeTest()
3 {
4 Assert.AreEqual(true, TypeExtension.IsNullableType(typeof(int?)));
5 Assert.AreEqual(false, TypeExtension.IsNullableType(typeof(int)));
6 Assert.AreEqual(true, TypeExtension.IsNullableType(typeof(Nullable<DateTime>)));
7 Assert.AreEqual(false, TypeExtension.IsNullableType(typeof(DateTime)));
8 }
9 [TestMethod()]
10 public void GetNonNullableTypeTest()
11 {
12 Assert.AreEqual(typeof(int), TypeExtension.GetNonNullableType(typeof(int?)));
13 Assert.AreEqual(typeof(DateTime), TypeExtension.GetNonNullableType(typeof(Nullable<DateTime>)));
14 }
15 [TestMethod()]
16 public void IsEnumerableTypeTest()
17 {
18 Assert.AreEqual(true, TypeExtension.IsEnumerableType(typeof(IEnumerable<string>)));
19 Assert.AreEqual(true, TypeExtension.IsEnumerableType(typeof(Collection<int>)));
20 }
21 [TestMethod()]
22 public void GetElementTypeTest()
23 {
24 Assert.AreEqual(typeof(int), TypeExtension.GetElementType(typeof(IEnumerable<int>)));
25 Assert.AreEqual(typeof(DateTime), TypeExtension.GetElementType(typeof(Collection<DateTime>)));
26 }
27 [TestMethod()]
28 public void IsKindOfGenericTest()
29 {
30 Assert.AreEqual(true, TypeExtension.IsKindOfGeneric(typeof(List<string>), typeof(IEnumerable<>)));
31 Assert.AreEqual(true, TypeExtension.IsKindOfGeneric(typeof(string), typeof(IComparable<>)));
32 }
33 [TestMethod()]
34 public void FindGenericTypeTest()
35 {
36 Assert.AreEqual(typeof(IEnumerable<string>),
37 TypeExtension.FindGenericType(typeof(IEnumerable<>), typeof(List<string>)));
38 }
代碼就是最好的文檔,想必大家已經都看明白了。
TypeHelper 是我從一個類庫中提取的,它原本是一個 internal static class,內部的方法同樣 internal static 。我僅僅是把 internal 改成了 public ,並在每個方法的第一個參數前加了個 this,最後給類中的方法按從簡到難的順序進行了排序。
也許是因為 TypeHelper 是一個內部類,並有強烈的應用語境,TypeHelper 並沒有采用契約式編程的方式,甚至在命名上也做了一些省略: 如 IsEnumerableType、GetElementType 兩個方法是實際是用來處理泛型集合類型的,但在方法名和參數名上並沒有“Generic”的字眼。(如果傳入非泛型類型或其它類型,將會產生難以預料的結果或異常。)
像這樣簡單將內部靜態類 TypeHelper 中的靜態方法公開為擴展方法是存在問題的,所以在應用之前,還得再做些工作。改進成為契約式編程的方式並不難,難的是給擴展方法起一個清晰明了簡單易懂的名字,否則就不要擴展了。IsEnumerableType、GetElementType 應該體現出是對泛型集合進行操作,簡單加上Generic字樣後名字好長,也不好理解。(大家如果能想出好的名字,請發在回復中,不勝感激!)
另外在使用時,我發現兩處“奇怪”的地方,如下圖:
調試至此處,type1為空,type2則是一個少見的奇怪類型。
TypeHelper 的出處我會在下一篇隨筆中進行說明。