2009-06-11 7 views
109

Vorrei eseguire un test se un oggetto è di tipo generico. Ho provato la seguente senza successo:Verificare se l'oggetto è di tipo generico in C#

public bool Test() 
{ 
    List<int> list = new List<int>(); 
    return list.GetType() == typeof(List<>); 
} 

Che cosa sto facendo male e come si esegue questo test?

risposta

153

Se si desidera verificare se si tratta di un'istanza di un tipo generico:

return list.GetType().IsGenericType; 

Se si desidera verificare se si tratta di una generica List<T>:

return list.GetType().GetGenericTypeDefinition() == typeof(List<>); 

Come Jon fa notare, questo controlli l'esatto tipo di equivalenza. Restituire false non significa necessariamente list is List<T> restituisce false (vale a dire che l'oggetto non può essere assegnato a una variabile List<T>).

+2

Tuttavia, questo non rileva i sottotipi. Vedi la mia risposta. È anche molto più difficile per le interfacce :( –

1
return list.GetType().IsGenericType; 
+2

"Is of a" not "Is a" – Gregory

+3

È corretto per una domanda diversa.Per questa domanda, è errato, in quanto affronta solo (significativamente meno di) metà del problema. – Groxx

+1

Stan La risposta di R in realtà risponde alla domanda come posta, ma ciò che l'OP intendeva veramente era "Testare se l'oggetto è di un tipo * particolare * generico in C#", per il quale questa risposta è davvero incompleta. – yoyo

68

presumo che non si vuole solo sapere se il tipo è generico, ma se un oggetto è un'istanza di un particolare tipo generico, senza conoscere gli argomenti di tipo.

Non è terribilmente semplice, sfortunatamente. Non è male se il tipo generico è una classe (come in questo caso) ma è più difficile per le interfacce. Ecco il codice per una classe:

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<string>())); 
     // 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<int>())); 
    } 

    class SubList : List<string> 
    { 
    } 

    class SubList<T> : List<T> 
    { 
    } 
} 

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 ci possono essere alcuni casi limite scomode intorno a questo, ma Non riesco a trovarne uno per il momento.

+0

Ho appena scoperto un problema con questo. Si riduce solo a una singola linea di eredità. Se, lungo il percorso, hai una base con sia una classe base * che * l'interfaccia che stai cercando, questo scende solo per il percorso della classe. – Groxx

+0

@Groxx: vero. Ho appena notato che lo dico nella risposta: "Non è male se il tipo generico è una classe (come in questo caso), ma è più difficile per le interfacce. Ecco il codice per una classe" –

+0

Ah , hai ragione. Ho perso la parte "per una classe". Grazie per il pezzo di codice, btw, è stato utile :) – Groxx

5

È possibile utilizzare il codice più corto usando parer dinamica questo può essere più lento di pura riflessione:

public static class Extension 
{ 
    public static bool IsGenericList(this object o) 
    { 
     return IsGeneric((dynamic)o); 
    } 

    public static bool IsGeneric<T>(List<T> o) 
    { 
     return true; 
    } 

    public static bool IsGeneric(object o) 
    { 
     return false; 
    } 
} 



var l = new List<int>(); 
l.IsGenericList().Should().BeTrue(); 

var o = new object(); 
o.IsGenericList().Should().BeFalse(); 
3

Queste sono le mie due metodi di estensione preferiti che coprono la maggior parte dei casi limite di controllo di tipo generico:

Funziona con :

  • multiple (generici) interfacce
  • classi
  • multipla (generica) di base
  • 0.123.
  • Ha un sovraccarico che sarà 'fuori' il tipo generico concreto se restituisce true (vedi test di unità 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 (base) funzionalità:

[Test] 
    public void SimpleGenericInterfaces() 
    { 
     Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>))); 
     Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>))); 

     Type concreteType; 
     Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>), out concreteType)); 
     Assert.AreEqual(typeof(IEnumerable<string>), concreteType); 

     Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>), out concreteType)); 
     Assert.AreEqual(typeof(IQueryable<string>), concreteType); 


    }