假设我有以下课程:
public class General<T> { }
我想知道一个对象是否属于那种类型。我知道我可以使用反射来确定对象是否属于该泛型类型Type.GetGenericTypeDefinition
,但我想避免这种情况。
是否有可能做类似的事情obj is General<T>
,或者obj.GetType().IsAssignableFrom(typeof(General<T>))
?
我很惊讶我找不到类似的问题,尽管我可能在搜索中使用了错误的关键字。
你可以这样做:
var obj = new General<int>();
var type = obj.GetType();
var isGeneral =
(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(General<>)) ||
type.GetBaseTypes().Any(x => x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(General<>));
GetBaseTypes
以下扩展方法在哪里:
public static IEnumerable<Type> GetBaseTypes(this Type type)
{
if (type.BaseType == null) return type.GetInterfaces();
return new []{type}.Concat(
Enumerable.Repeat(type.BaseType, 1)
.Concat(type.GetInterfaces())
.Concat(type.GetInterfaces().SelectMany<Type, Type>(GetBaseTypes))
.Concat(type.BaseType.GetBaseTypes()));
}
归功于Slacks 的回答
类似 问题有很多答案,但它们都需要反思才能向上走类型层次结构。我怀疑没有更好的方法。如果性能很关键,缓存结果可能是一种选择。这是一个使用 aConcurrentDictionary
作为简单缓存的示例。然后将成本降低为简单的类型查找(通过GetType
)和ConcurrentDictionary
缓存初始化后的查找。
using System.Collections.Concurrent;
private static ConcurrentDictionary<Tuple<Type,Type>, bool> cache = new ConcurrentDictionary<Tuple<Type,Type>, bool>();
public static bool IsSubclassOfRawGeneric(this Type toCheck, Type generic) {
var input = Tuple.Create(toCheck, generic);
bool isSubclass = cache.GetOrAdd(input, key => IsSubclassOfRawGenericInternal(toCheck, generic));
return isSubclass;
}
private static bool IsSubclassOfRawGenericInternal(Type toCheck, Type generic) {
while (toCheck != null && toCheck != typeof(object)) {
var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
if (generic == cur) {
return true;
}
toCheck = toCheck.BaseType;
}
return false;
}
你会像这样使用它:
class I : General<int> { }
object o = new I();
Console.WriteLine(o is General<int>); // true
Console.WriteLine(o.GetType().IsSubclassOfRawGeneric(typeof(General<>))); //true
如果泛型类或接口具有可以被代码使用的成员,这些成员以更通用的形式保存引用,Object
但没有可用的实际泛型类型,则此类成员应在非泛型基类或接口中公开。该框架在许多情况下未能遵守这一原则,但没有理由必须效仿他们。例如,像这样的类型IList<T>
可以派生自IListBase
其中包含或继承的成员,例如:
int Count {get;}
void Delete(int index);
void Clear();
void Swap(int index1, int index2);
int Compare(int index1, int index2);
// Return an object with a `StoreToIndex(int)` method
// which would store it to the list it came from.
ListItemHolder GetItemHolder(int index);
ListFeatures Features {get;}
这些成员都不会以任何方式依赖列表中包含的项目类型,并且可以编写方法来执行诸如排序列表之类的事情(如果它Features
表明它是可写的并且知道如何比较项目)而不必知道关于元素类型的任何信息。如果泛型接口继承自非泛型接口,则需要非泛型函数的代码可以简单地转换为非泛型接口类型并直接使用它。
使用类型参数实例化的泛型类型定义与其他泛型类型实例完全没有关系。它们也与泛型类型定义无关。在分配和运行时强制转换方面,它们完全不兼容。如果不是这样,就有可能破坏类型系统。
因此,运行时强制转换无济于事。您确实将不得不求助于Type.GetGenericTypeDefinition
. 您可以将其抽象为辅助函数,并以这种方式保持代码相对干净。
对于更通用的解决方案,它适用于任何父类型(基类和接口):
public static bool IsCompatibleWith(this Type type, Type parentType)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
if (parentType.IsAssignableFrom(type))
{
return true;
}
return type.GetAssignableTypes()
.Where(t => t.IsGenericType)
.Any(t=> t.GetGenericTypeDefinition() == parentType);
}
/// <summary>
/// Gets all parent types including the currrent type.
/// </summary>
public static IEnumerable<Type> GetAssignableTypes(this Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
// First check for interfaces because interface types don't have base classes.
foreach (Type iType in type.GetInterfaces())
{
yield return iType;
}
// Then check for base classes.
do
{
yield return type;
type = type.BaseType;
}
while (type != null);
}
想出更好的方法名称。也许称它为IsCompatibleWith
误导。也许IsKindOf
?此外,GetAssignableTypes
也可以调用GetParentTypes
,但这也具有误导性。起名很难。记录它更好。
一些测试:
IsCompatibleWith(typeof(List<int>), typeof(IList<int>))
真的
IsCompatibleWith(typeof(List<>), typeof(IList<>))
真的
IsCompatibleWith(typeof(List<int>), typeof(IList<>))
真的
IsCompatibleWith(typeof(List<int>), typeof(IList<string>))
错误的