说我有以下课程:
public class General<T> { }
我想知道对象是否属于那种类型。
我知道我可以使用反射来查明该对象是否属于Type.GetGenericTypeDefinition
的泛型类型,但我想避免这种情况。
是否可以执行obj is General<T>
或obj.GetType().IsAssignableFrom(typeof(General<T>))
?
我很惊讶我无法找到类似的问题,尽管我的搜索中可能使用了错误的关键字。
答案 0 :(得分:7)
你可以这样做:
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()));
}
答案 1 :(得分:1)
similar questions有很多答案,但它们都需要反思才能走向类型层次结构。我怀疑没有更好的方法。如果性能至关重要,可以选择缓存结果。以下是使用ConcurrentDictionary
作为简单缓存的示例。然后在初始化缓存后将成本简化为简单类型查找(通过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
答案 2 :(得分:0)
使用类型参数实例化的通用类型定义与其他泛型类型实例完全没有关系。它们也与泛型类型定义无关。在分配和运行时转换时,它们完全不兼容。如果不是,就有可能破坏类型系统。
因此,运行时强制转换无效。你确实不得不诉诸Type.GetGenericTypeDefinition
。你可以将它抽象为一个辅助函数,并保持你的代码相对干净。
答案 3 :(得分:0)
如果泛型类或接口具有可由代码使用的成员,这些代码以更通用的形式(例如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
表示它是可写的并知道如何比较项目,而不必知道有关元素类型的任何信息。如果通用接口继承自非泛型接口,则需要非泛型函数的代码可以简单地转换为非泛型接口类型并直接使用它。
答案 4 :(得分:0)
对于更通用的解决方案,它可以与任何父类型(基类以及接口)一起使用:
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>))
错误