使用反射,我试图找到从给定基类继承的类型集。花了很长时间才弄清楚简单的类型,但是当涉及到泛型时我很难过。
对于这段代码,第一个IsAssignableFrom返回true,但第二个返回false。然而,最终的任务编译得很好。
class class1 { }
class class2 : class1 { }
class generic1<T> { }
class generic2<T> : generic1<T> { }
class Program
{
static void Main(string[] args)
{
Type c1 = typeof(class1);
Type c2 = typeof(class2);
Console.WriteLine("c1.IsAssignableFrom(c2): {0}", c1.IsAssignableFrom(c2));
Type g1 = typeof(generic1<>);
Type g2 = typeof(generic2<>);
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));
generic1<class1> cc = new generic2<class1>();
}
}
那么我如何在运行时确定一个泛型类型定义是否来自另一个?
答案 0 :(得分:76)
public static bool IsAssignableToGenericType(Type givenType, Type genericType)
{
var interfaceTypes = givenType.GetInterfaces();
foreach (var it in interfaceTypes)
{
if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
return true;
}
if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
return true;
Type baseType = givenType.BaseType;
if (baseType == null) return false;
return IsAssignableToGenericType(baseType, genericType);
}
(如果您喜欢这个答案,请提供相关链接的答案,因为代码不是我的。)
答案 1 :(得分:11)
您发布的确切代码并未产生令人惊讶的结果。
这说“假”:
Type g1 = typeof(generic1<>);
Type g2 = typeof(generic2<>);
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));
这说“是”:
Type g1 = typeof(generic1<class1>);
Type g2 = typeof(generic2<class1>);
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));
不同之处在于,开放泛型类型不能包含实例,因此一个不能“分配”给另一个实例。
来自docs:
如果
true
和当前,则返回c
Type
代表相同类型,或者如果 当前Type
在c
的继承层次结构,或者是 当前Type
是一个接口c
实现的,或c
是否为Type
泛型类型参数和当前c
代表其中一个false
的约束。c
如果没有 这些条件是真的,或null
是MyGenericList<int>
。
在这种情况下,显然这些条件都不属实。还有一个额外的注意事项:
泛型类型定义不是 可从封闭的构造中分配 类型。也就是说,你不能分配 封闭式建筑
MyGenericList(Of Integer)
(在Visual Basic中为MyGenericList<T>
)到a 类型为{{1}}的变量。
答案 2 :(得分:2)
在下面的例子中使用Konrad Rudolph提供的方法可能是错误的,例如:IsAssignableToGenericType(typeof(A),typeof(A&lt;&gt;)); // return false
我认为这是一个更好的答案
public static bool IsAssignableFrom(Type extendType, Type baseType)
{
while (!baseType.IsAssignableFrom(extendType))
{
if (extendType.Equals(typeof(object)))
{
return false;
}
if (extendType.IsGenericType && !extendType.IsGenericTypeDefinition)
{
extendType = extendType.GetGenericTypeDefinition();
}
else
{
extendType = extendType.BaseType;
}
}
return true;
}
测试用例,请参阅Using IsAssignableFrom with C# generics了解详细信息
using System;
/**
* Sam Sha - yCoder.com
*
* */
namespace Test2
{
class MainClass
{
public static void Main (string[] args)
{
string a = "ycoder";
Console.WriteLine(a is object);
A aa = new A();
//Console.WriteLine(aa is A<>);//con't write code like this
typeof(A<>).IsAssignableFrom(aa.GetType());//return false
Trace(typeof(object).IsAssignableFrom(typeof(string)));//true
Trace(typeof(A<>).IsAssignableFrom(typeof(A)));//false
AAA aaa = new AAA();
Trace("Use IsTypeOf:");
Trace(IsTypeOf(aaa, typeof(A<>)));
Trace(IsTypeOf(aaa, typeof(AA)));
Trace(IsTypeOf(aaa, typeof(AAA<>)));
Trace("Use IsAssignableFrom from stackoverflow - not right:");
Trace(IsAssignableFrom(typeof(A), typeof(A<>))); // error
Trace(IsAssignableFrom(typeof(AA), typeof(A<>)));
Trace(IsAssignableFrom(typeof(AAA), typeof(A<>)));
Trace("Use IsAssignableToGenericType:");
Trace(IsAssignableToGenericType(typeof(A), typeof(A<>)));
Trace(IsAssignableToGenericType(typeof(AA), typeof(A<>)));
Trace(IsAssignableToGenericType(typeof(AAA), typeof(A<>)));
}
static void Trace(object log){
Console.WriteLine(log);
}
public static bool IsTypeOf(Object o, Type baseType)
{
if (o == null || baseType == null)
{
return false;
}
bool result = baseType.IsInstanceOfType(o);
if (result)
{
return result;
}
return IsAssignableFrom(o.GetType(), baseType);
}
public static bool IsAssignableFrom(Type extendType, Type baseType)
{
while (!baseType.IsAssignableFrom(extendType))
{
if (extendType.Equals(typeof(object)))
{
return false;
}
if (extendType.IsGenericType && !extendType.IsGenericTypeDefinition)
{
extendType = extendType.GetGenericTypeDefinition();
}
else
{
extendType = extendType.BaseType;
}
}
return true;
}
//from stackoverflow - not good enough
public static bool IsAssignableToGenericType(Type givenType, Type genericType) {
var interfaceTypes = givenType.GetInterfaces();
foreach (var it in interfaceTypes)
if (it.IsGenericType)
if (it.GetGenericTypeDefinition() == genericType) return true;
Type baseType = givenType.BaseType;
if (baseType == null) return false;
return baseType.IsGenericType &&
baseType.GetGenericTypeDefinition() == genericType ||
IsAssignableToGenericType(baseType, genericType);
}
}
class A{}
class AA : A{}
class AAA : AA{}
}
答案 3 :(得分:0)
您需要比较包含的类型。请参阅:How to get the type of T from a member of a generic class or method?
换句话说,我认为您需要检查泛型类包含的类型是否可赋值,而不是泛型类本身。
答案 4 :(得分:0)
我有另一种方法可以解决此问题,这是我的课程
public class Signal<T>{
protected string Id {get; set;} //This must be here, I use a property because MemberInfo is returned in an array via GetMember() reflection function
//Some Data and Logic And stuff that involves T
}
public class OnClick : Signal<string>{}
现在,如果我有一个OnClick类型的实例,但我不知道,我想确定我是否有一个实例继承自任何类型的Signal <>?我这样做
Type type = GetTypeWhomISuspectMightBeAGenericSignal();
PropertyInfo secretProperty = type.GetProperty("Id", BindingFlags.NonPublic | BindingFlags.Instance);
Type SpecificGenericType = secretProperty.DeclaringType; //This is the trick
bool IsMyTypeInheriting = SpecificGenericType.IsGenericType && SpecificGenericType.GetGenericTypeDefinition() == typeof(Signal<>); //This way we are getting the genericTypeDefinition and comparing it to any other genericTypeDefinition of the same argument length.
所以这对我有用,它不是递归的,它通过指定的属性使用了一个技巧。它的局限性在于很难编写一个功能来检查所有泛型的可分配性。但是对于特定类型它可以工作
很显然,您需要检查if()的条件是否更好以及是否合适,但这是通过这种方式评估类型对其基本泛型的可分配性所需的Raw行。
希望这会有所帮助
答案 5 :(得分:0)
我的两分钱。恕我直言,分离IsAssignableFrom的实现,派生或原始功能没有太大意义,
根据先前给出的答案进行构建,这就是我的方法:
public static bool ImplementsOrDerives(this Type @this, Type from)
{
if(from is null)
{
return false;
}
else if(!from.IsGenericType)
{
return from.IsAssignableFrom(@this);
}
else if(!from.IsGenericTypeDefinition)
{
return from.IsAssignableFrom(@this);
}
else if(from.IsInterface)
{
foreach(Type @interface in @this.GetInterfaces())
{
if(@interface.IsGenericType && @interface.GetGenericTypeDefinition() == from)
{
return true;
}
}
}
if(@this.IsGenericType && @this.GetGenericTypeDefinition() == from)
{
return true;
}
return @this.BaseType?.ImplementsOrDerives(from) ?? false;
}
答案 6 :(得分:0)
@konrad_ruldolph 的回答大部分是正确的,但它要求您知道基本类型/接口是一个开放的泛型。我提出了一种改进,将非泛型测试与循环相结合来测试泛型匹配。
public static class Ext
{
public static bool IsAssignableToGeneric(
this Type assignableFrom,
Type assignableTo)
{
bool IsType(Type comparand)
=> assignableTo.IsAssignableFrom(comparand)
|| (comparand.IsGenericType
&& comparand.GetGenericTypeDefinition() == assignableTo);
while (assignableFrom != null)
{
if (IsType(assignableFrom)
|| assignableFrom
.GetInterfaces()
.Any(IsType))
{
return true;
}
assignableFrom = assignableFrom.BaseType;
}
return false;
}
}