Vorrei eseguire un test se un object è di tipo generico. Ho provato il seguente senza successo:
public bool Test() { List list = new List(); return list.GetType() == typeof(List); }
Cosa sto sbagliando e come eseguo questo test?
Se si desidera verificare se si tratta di un’istanza di tipo generico:
return list.GetType().IsGenericType;
Se vuoi controllare se è un List
generico List
:
return list.GetType().GetGenericTypeDefinition() == typeof(List<>);
Come sottolinea Jon, questo controlla l’esatta equivalenza del tipo. Restituire false
non significa necessariamente che l’ list is List
restituisce false
(ovvero l’object non può essere assegnato a una variabile List
).
Suppongo che tu non voglia solo sapere se il tipo è generico, ma se un object è un’istanza di un particolare tipo generico, senza conoscere gli argomenti di tipo.
Non è terribilmente semplice, sfortunatamente. Non è male se il tipo generico è una class (come in questo caso) ma è più difficile per le interfacce. Ecco il codice per una class:
using System; using System.Collections.Generic; using System.Reflection; class Test { static bool IsInstanceOfGenericType(Type genericType, object instance) { Type type = instance.GetType(); while (type != null) { if (type.IsGenericType && type.GetGenericTypeDefinition() == genericType) { return true; } type = type.BaseType; } return false; } static void Main(string[] args) { // True Console.WriteLine(IsInstanceOfGenericType(typeof(List<>), new List())); // False Console.WriteLine(IsInstanceOfGenericType(typeof(List<>), new string[0])); // True Console.WriteLine(IsInstanceOfGenericType(typeof(List<>), new SubList())); // True Console.WriteLine(IsInstanceOfGenericType(typeof(List<>), new SubList())); } class SubList : List { } class SubList : List { } }
EDIT: come notato nei commenti, questo può funzionare per le interfacce:
foreach (var i in type.GetInterfaces()) { if (i.IsGenericType && i.GetGenericTypeDefinition() == genericType) { return true; } }
Ho un vago sospetto che ci possano essere alcuni casi imbarazzanti riguardo a questo, ma non riesco a trovarne uno che fallisce proprio adesso.
È ansible utilizzare un codice più breve utilizzando un’attenuazione dynamic che potrebbe essere più lenta del puro riflesso:
public static class Extension { public static bool IsGenericList(this object o) { return IsGeneric((dynamic)o); } public static bool IsGeneric(List o) { return true; } public static bool IsGeneric( object o) { return false; } } var l = new List(); l.IsGenericList().Should().BeTrue(); var o = new object(); o.IsGenericList().Should().BeFalse();
Questi sono i miei due metodi di estensione preferiti che coprono la maggior parte dei casi limite di controllo di tipo generico:
Lavora con:
Ha un sovraccarico che “fuori” il tipo generico concreto se restituisce true (vedi test unitario per i campioni):
public static bool IsOfGenericType(this Type typeToCheck, Type genericType) { Type concreteType; return typeToCheck.IsOfGenericType(genericType, out concreteType); } public static bool IsOfGenericType(this Type typeToCheck, Type genericType, out Type concreteGenericType) { while (true) { concreteGenericType = null; if (genericType == null) throw new ArgumentNullException(nameof(genericType)); if (!genericType.IsGenericTypeDefinition) throw new ArgumentException("The definition needs to be a GenericTypeDefinition", nameof(genericType)); if (typeToCheck == null || typeToCheck == typeof(object)) return false; if (typeToCheck == genericType) { concreteGenericType = typeToCheck; return true; } if ((typeToCheck.IsGenericType ? typeToCheck.GetGenericTypeDefinition() : typeToCheck) == genericType) { concreteGenericType = typeToCheck; return true; } if (genericType.IsInterface) foreach (var i in typeToCheck.GetInterfaces()) if (i.IsOfGenericType(genericType, out concreteGenericType)) return true; typeToCheck = typeToCheck.BaseType; } }
Ecco un test per dimostrare la funzionalità (di base):
[Test] public void SimpleGenericInterfaces() { Assert.IsTrue(typeof(Table).IsOfGenericType(typeof(IEnumerable<>))); Assert.IsTrue(typeof(Table ).IsOfGenericType(typeof(IQueryable<>))); Type concreteType; Assert.IsTrue(typeof(Table ).IsOfGenericType(typeof(IEnumerable<>), out concreteType)); Assert.AreEqual(typeof(IEnumerable ), concreteType); Assert.IsTrue(typeof(Table ).IsOfGenericType(typeof(IQueryable<>), out concreteType)); Assert.AreEqual(typeof(IQueryable ), concreteType); }
return list.GetType().IsGenericType;