System.Type 提供豐富反射功能,但在處理泛型類型 (可空類型、集合介面) 時仍有缺口。本文打造 TypeHelper 擴展類別,補強泛型類型判斷與元素類型提取能力。
using System;
using System.Collections.Generic;
using System.Reflection;
public static class TypeHelper
{
// 判斷是否為可空類型 (Nullable<T>)
public static bool IsNullableType(this Type type)
=> type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
// 取得可空類型的底層類型 (int? -> int)
public static Type GetNonNullableType(this Type type)
=> IsNullableType(type) ? type.GetGenericArguments()[0] : type;
// 判斷是否實作 IEnumerable<T> (泛型集合)
public static bool IsEnumerableType(this Type type)
=> FindGenericType(typeof(IEnumerable<>), type) != null;
// 取得 IEnumerable<T> 的元素類型
public static Type GetElementType(this Type type)
{
var genericType = FindGenericType(typeof(IEnumerable<>), type);
return genericType != null ? genericType.GetGenericArguments()[0] : type;
}
// 判斷是否為某泛型定義的實作 (如 List<string> 是 IEnumerable<> 的一種)
public static bool IsKindOfGeneric(this Type type, Type genericDefinition)
=> FindGenericType(genericDefinition, type) != null;
// 核心遞迴搜尋:在型別繼承鏈與介面中尋找符合的泛型定義
private static Type FindGenericType(Type definition, Type type)
{
while (type != null && type != typeof(object))
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == definition)
return type;
if (definition.IsInterface)
{
foreach (var iface in type.GetInterfaces())
{
var found = FindGenericType(definition, iface);
if (found != null) return found;
}
}
type = type.BaseType;
}
return null;
}
}
[TestClass]
public class TypeHelperTests
{
[TestMethod]
public void NullableTypeTests()
{
Assert.IsTrue(typeof(int?).IsNullableType());
Assert.IsFalse(typeof(int).IsNullableType());
Assert.IsTrue(typeof(Nullable<DateTime>).IsNullableType());
Assert.AreEqual(typeof(int), typeof(int?).GetNonNullableType());
Assert.AreEqual(typeof(DateTime), typeof(Nullable<DateTime>).GetNonNullableType());
}
[TestMethod]
public void EnumerableTypeTests()
{
Assert.IsTrue(typeof(IEnumerable<string>).IsEnumerableType());
Assert.IsTrue(typeof(List<int>).IsEnumerableType());
Assert.IsTrue(typeof(Collection<DateTime>).IsEnumerableType());
Assert.AreEqual(typeof(int), typeof(IEnumerable<int>).GetElementType());
Assert.AreEqual(typeof(DateTime), typeof(Collection<DateTime>).GetElementType());
}
[TestMethod]
public void GenericKindTests()
{
Assert.IsTrue(typeof(List<string>).IsKindOfGeneric(typeof(IEnumerable<>)));
Assert.IsTrue(typeof(string).IsKindOfGeneric(typeof(IComparable<>)));
Assert.AreEqual(typeof(IEnumerable<string>), typeof(List<string>).FindGenericType(typeof(IEnumerable<>)));
}
}
IRepository<T>) 進行自動註冊。IsEnumerableType/GetElementType 僅針對 IEnumerable<T>,非泛型 IEnumerable (如 ArrayList) 會回傳 false/原型別。int[]) 實作 IEnumerable<T>,可正常運作。FindGenericType 結果。這組 Type 擴展方法解決了泛型類型分析的常見痛點,是建構反射密集型框架 (MVC、WebAPI、ORM、DI 容器) 的利器。記得搭配單元測試確保邊界條件正確處理。