Использование IsAssignableFrom с открытыми типами
Используя отражение, я пытаюсь найти набор типов, которые наследуются от данного базового класса. Это не заняло много времени, чтобы разобраться в простых типах, но я в тупике, когда дело доходит до дженериков.
Для этого фрагмента кода первый 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>();
}
}
Итак, как мне определить во время выполнения, выводится ли одно общее определение типа из другого?
Ответы
Ответ 1
Из ответа на другой вопрос:
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);
}
(Если вам нравится ответ, пожалуйста, поддержите связанный ответ, так как код не мой.)
Ответ 2
Точный код, который вы отправили, не возвращает неожиданных результатов.
Это говорит "false":
Type g1 = typeof(generic1<>);
Type g2 = typeof(generic2<>);
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));
Это говорит "true":
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>
.
Ответ 3
В следующем случае использование метода, предоставленного Konrad Rudolph, может быть неправильным, например: IsAssignableToGenericType (typeof (A), typeof (A < > ));//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;
}
тестовый пример, см. Использование IsAssignableFrom с С# 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{}
}
Ответ 4
Вам нужно сравнить содержащийся тип. Смотрите: Как получить тип T от члена общего класса или метода?
Другими словами, я думаю, вам нужно проверить, является ли тип, содержащийся в родовом классе, назначаемым, а не сам общий класс.