Проверьте, не является ли объект неспецифическим родовым типом в С#
Скажем, у меня есть следующий класс:
public class General<T> { }
И я хочу узнать, имеет ли объект этот тип.
Я знаю, что могу использовать отражение, чтобы выяснить, имеет ли объект этот общий тип с Type.GetGenericTypeDefinition
, но я хочу этого избежать.
Можно ли сделать что-то вроде obj is General<T>
или obj.GetType().IsAssignableFrom(typeof(General<T>))
?
Я очень удивлен, что я не мог найти подобный вопрос, хотя, возможно, я использовал неправильные ключевые слова в моих поисках.
Ответы
Ответ 1
Вы можете сделать это:
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()));
}
кредиты Ответы на слаксы
Ответ 2
Есть много ответов на похожие 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
Ответ 3
Определения общего типа, которые создаются с помощью параметров типа, не имеют никакого отношения к другим экземплярам универсального типа. Они также не имеют никакого отношения к определению общего типа. Они полностью несовместимы, когда речь идет о назначении и времени выполнения. Если бы они не были, можно было бы разбить систему типов.
По этой причине выполнение выполнения не поможет. Вам придётся прибегнуть к Type.GetGenericTypeDefinition
. Вы можете абстрагировать это на вспомогательную функцию и сохранить свой код относительно чистым таким образом.
Ответ 4
Если общий класс или интерфейс имеют члены, которые могут использоваться кодом, который содержал ссылку в более общей форме типа 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
указал, что он доступен для записи и умеет сравнивать элементы), не зная ничего о типе элемента. Если общий интерфейс наследуется от не-общего интерфейса, код, требующий не общих функций, может просто быть перенесен в не общий тип интерфейса и использовать его напрямую.