2009-07-24 6 views

Répondre

8

en fait, cela fonctionne:

public static bool IsGenericList(Type type) 
{ 
    if (!type.IsGenericType) 
    return false; 
    var genericArguments = type.GetGenericArguments(); 
    if (genericArguments.Length != 1) 
    return false; 

    var listType = typeof (IList<>).MakeGenericType(genericArguments); 
    return listType.IsAssignableFrom(type); 
} 
+0

Cela fonctionne pour ce cas particulier, mais il manquera des choses comme MyIntList etc. comme indiqué dans la réponse de Jon. J'ai posté une autre réponse ci-dessous pour essayer d'expliquer la relation. – Avish

2

Je suppose que la méthode n'a pas vraiment de sens, car une instance est jamais du type générique - elle est toujours construite avec un argument de type particulier. En d'autres termes, vous ne pourriez jamais avoir une variable "ouverte" à assigner dans, ni une référence à une instance ouverte à utiliser comme valeur pour l'assignation.

Comme vous le dites, vous ne savez pas si les paramètres de type seront les mêmes - si (par exemple), vous pouvez définir:

class BizarreList<T> : IList<int> 

Il se sent comme il devrait y avoir une certaine façon d'exprimer la relation si ...

+0

peut vraiment cette relation s'exprimer? Comme vous l'avez dit, il n'y aura jamais d'instance de type générique, seulement des instances d'un type construit fermé. Donc, 'IList <>' peut-il être assignable à partir de List <> '? Je dis non parce que cela ne peut être vrai que dans le cas où les deux types ont été construits avec un argument de type commun et comme nous l'avons déjà souligné, il n'y a pas d'argument de type à avoir. Ceci est un problème très intéressant cependant :) –

+0

Il serait agréable de pouvoir détecter que la liste implémente IList pour tous les T, plutôt que de toujours implémenter IList . Je pense que cela peut être fait avec suffisamment d'efforts, mais ce serait douloureux. Fondamentalement, les API de réflexion ne font pas un très bon travail avec les génériques, IMO. –

+0

Voir la réponse J'ai posté pour le code qui exprime cette relation. Il peut y avoir des cas limites, mais je ne les vois pas. – ripper234

3

cela a vraiment à voir avec les types ouverts construits.

Quand vous dites:

class List<T> : IList<T> 

Vous êtes en train de dire: ma classe est appelée Liste, il a un paramètre de type appelé T, et il implémente l'interface qui est construit à partir IList <> en utilisant la même T. Ainsi, le T dans la partie définition et le T dans la partie "implémente" font tous deux référence au même paramètre de type - vous le déclarez avant le signe deux-points, puis vous le référenciez immédiatement après le signe deux-points.

Cela devient confus car le paramètre de type IList<> est également appelé T - mais c'est un paramètre de type différent entièrement. Donc, nous allons re-proclamons notre classe concrète comme ceci:

class List<U> : IList<U> 

Ceci est tout à fait équivalent à ce qui précède, que maintenant nous pouvons dire « U » lorsque l'on se réfère au paramètre de type de liste, et T lorsque nous parlons de celui de IList. Ils sont différents types.

Maintenant, il devient plus facile de voir pourquoi la définition de type générique List<U> (qui est ce que vous voulez dire quand vous dites typeof(List<>)) ne met pas en œuvre la définition de type generifc IList<T> (qui est ce que vous voulez dire quand vous dites typeof(IList<>)), mais plutôt implémente le type construit générique ouvert IList<U> (c'est-à-dire, IList construit avec le propre type de parémètre de List). Donc, fondamentalement, les définitions de types génériques n'héritent ou n'implémentent jamais d'autres définitions de types génériques - elles implémentent généralement des types construits ouverts en utilisant leurs propres paramètres de type avec d'autres définitions de types génériques.

La réponse de Ripper234 montre comment gérer ce cas particulier en utilisant Reflection, donc je ne vais pas le répéter; Je voulais juste clarifier la relation entre ces types, et j'espère que cela est sorti au moins quelque peu intelligible.

0

Voici la méthode d'extension de AutoMapper:

public static bool IsCollectionType(this Type type) 
    { 
     if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ICollection<>)) 
     { 
      return true; 
     } 

     IEnumerable<Type> genericInterfaces = type.GetInterfaces().Where(t => t.IsGenericType); 
     IEnumerable<Type> baseDefinitions = genericInterfaces.Select(t => t.GetGenericTypeDefinition()); 

     var isCollectionType = baseDefinitions.Any(t => t == typeof(ICollection<>)); 

     return isCollectionType; 
    } 
Questions connexes