.NET 中提供了很多判斷某個類型或實例是某個類的子類或某個接口的實現類的方法,然而這事情一旦牽扯到泛型就沒那么省心了。
本文將提供判斷泛型接口實現或泛型類型子類的方法。
本文內容
.NET 中沒有自帶的方法
對於實例,.NET 中提供了這些方法來判斷:
if (instance is Foo || instance is IFoo) { }
對於類型,.NET 中提供了這些方法來判斷:
if (typeof(Foo).IsAssignableFrom(type) || typeof(IFoo).IsAssignableFrom(type)) { }
或者,如果不用判斷接口,只判斷類型的話:
if (type.IsSubClassOf(typeof(Foo))) { }
對於 typeof 關鍵字,不止可以寫 typeof(Foo),還可以寫 typeof(Foo<>)。這可以得到泛型版本的 Foo<T> 的類型。
不過,如果你試圖拿這個泛型版本的 typeof(Foo<>) 執行上述所有判斷,你會發現所有的 if 條件都會是 false。
我們需要自己編寫方法
typeof(Foo<>) 和 typeof(Foo<SomeClass>) 之間的關系就是 GetGenericTypeDefinition 函數帶來的關系。
所以我們可以充分利用這一點完成泛型類型的判斷。
比如,我們要判斷接口:
public static bool HasImplementedRawGeneric(this Type type, Type generic) { // 遍歷類型實現的所有接口,判斷是否存在某個接口是泛型,且是參數中指定的原始泛型的實例。 return type.GetInterfaces().Any(x => generic == (x.IsGenericType ? x.GetGenericTypeDefinition() : x)); }
而如果需要判斷類型,那么就需要遍歷此類的基類了:
public static bool IsSubClassOfRawGeneric([NotNull] this Type type, [NotNull] Type generic) { if (type == null) throw new ArgumentNullException(nameof(type)); if (generic == null) throw new ArgumentNullException(nameof(generic)); while (type != null && type != typeof(object)) { isTheRawGenericType = IsTheRawGenericType(type); if (isTheRawGenericType) return true; type = type.BaseType; } return false; bool IsTheRawGenericType(Type test) => generic == (test.IsGenericType ? test.GetGenericTypeDefinition() : test); }
於是,我們可以把這兩個方法合成一個,用於實現類似 IsAssignableFrom 的效果,不過這回將支持原始接口(也就是 typeof(Foo<>))。
/// <summary> /// 判斷指定的類型 <paramref name="type"/> 是否是指定泛型類型的子類型,或實現了指定泛型接口。 /// </summary> /// <param name="type">需要測試的類型。</param> /// <param name="generic">泛型接口類型,傳入 typeof(IXxx<>)</param> /// <returns>如果是泛型接口的子類型,則返回 true,否則返回 false。</returns> public static bool HasImplementedRawGeneric([NotNull] this Type type, [NotNull] Type generic) { if (type == null) throw new ArgumentNullException(nameof(type)); if (generic == null) throw new ArgumentNullException(nameof(generic)); // 測試接口。 var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType); if (isTheRawGenericType) return true; // 測試類型。 while (type != null && type != typeof(object)) { isTheRawGenericType = IsTheRawGenericType(type); if (isTheRawGenericType) return true; type = type.BaseType; } // 沒有找到任何匹配的接口或類型。 return false; // 測試某個類型是否是指定的原始接口。 bool IsTheRawGenericType(Type test) => generic == (test.IsGenericType ? test.GetGenericTypeDefinition() : test); }
