2009-12-04 4 views
5

Quelqu'un at-il entendu parler d'un "dictionnaire de types" qui utilise les types comme clés et prend en charge l'héritage?Type Dictionnaire?

Dans ma demande, je voudrais avoir un dictionnaire de types à des fonctions, un peu comme ceci:

Dictionary<Type, Func<object, object>> Transformers; 

L'idée est qu'il serait utilisé pour transformer un objet d'une certaine façon en fonction de sa type:

// Transform an object 'obj' 
object result = Transformers[obj.GetType()](obj) 

Un dictionnaire ordinaire présente l'inconvénient que le type doit correspondre exactement. Donc, si j'ai écrit un transformateur pour IList < T>, il est inutile de le placer dans le dictionnaire Transformers car aucun objet n'a le type IList < T> (seulement T [], Liste < T>, etc.) En d'autres termes , si obj est une liste < T>, le transformateur pour IList < T> ne sera pas trouvé par une recherche dans un dictionnaire ordinaire. En supposant qu'il n'y ait pas de TypeDictionary < TValue>, je pourrais envisager d'en écrire un si ce n'est pas trop difficile. Des idées comment cela pourrait être accompli?

Répondre

2

Vous devriez pouvoir utiliser un dictionnaire avec custom comparer qui utilise Type.IsAssignableFrom pour comparer les clés. Comme Qwertie l'a fait remarquer, cela ne fonctionne pas car vous ne pouvez pas implémenter un calcul de code de hachage répétable basé sur un type, ses interfaces et ses classes d'ancêtres. His answer fournit une solution possible en effectuant à plusieurs reprises des recherches de table de hachage pour le type, les interfaces et les classes d'ancêtres jusqu'à ce qu'il trouve une correspondance.

Le seul problème de cette solution est que vous n'avez aucun moyen de spécifier quelle correspondance doit être effectuée lorsqu'il y a plusieurs correspondances. Si vous avez besoin de cette flexibilité et de ce contrôle, je vous suggère de considérer le modèle de conception chain-of-responsibility. Chaque transformateur peut être un lien dans la chaîne, et il est responsable de déterminer s'il peut être appliqué à l'objet. Sinon, il passe la requête au lien suivant. L'ordre des transformateurs dans la chaîne détermine la priorité. Vous perdez la vitesse d'une table de hachage, mais vous perdiez une partie de cette vitesse à cause de plusieurs recherches.

+1

Cela ne fonctionnerait pas. Quel code de hachage IEqualityComparer renvoie-t-il pour une classe B dérivée de A et implémente IA et IB? Aussi, gardez à l'esprit que le dictionnaire devrait être capable de contenir des clés pour 'List ', 'IList ' et 'object' simultanément. – Qwertie

+0

Vous avez raison, je n'ai pas réfléchi à ça. –

+0

@Qwertie dans ce cas, quel serait votre dictionnaire s'il y avait plusieurs correspondances dans le dictionnaire pour un type? Devrait-il retourner toutes les occurrences, seulement les plus spécialisées, etc.? –

1

Il me semble que le poseur de dictionnaire n'a pas une sémantique différente d'un dictionnaire ordinaire, donc une approche consiste à utiliser un dictionnaire standard avec recherche spécialisée:

public class TypeDictionary<TValue> : Dictionary<Type, TValue> 
{ 
    public new TValue this[Type key] 
    { 
     get { 
      TValue value; 
      if (TryGetValue(key, out value)) 
       return value; 
      throw new KeyNotFoundException("Not found: " + key.Name); 
     } 
    } 
    public new bool TryGetValue(Type key, out TValue value) 
    { 
     if (base.TryGetValue(key, out value)) 
      return true; 

     Type[] interfaces = key.GetInterfaces(); 
     for (int i = 0; i < interfaces.Length; i++) 
      if (base.TryGetValue(interfaces[i], out value)) 
       return true; 

     Type @base = key.BaseType; 
     if (@base != null && TryGetValue(@base, out value)) 
      return true; 

     return false; 
    } 
} 

Notez que si une dérive de la classe B A partir de la classe A et des interfaces IA et IB, et une valeur est attribuée à chacun de ces types, elle est ambiguë: la valeur de A, IA ou IB doit-elle être retournée? L'implémentation ci-dessus choisit la première interface trouvée, et seulement si aucune interface n'est trouvée, elle recherche la classe de base.

Je n'ai aucune idée de la performance de ce dictionnaire. Si GetInterfaces() ou la propriété BaseType est lente, les performances de recherche seront très mauvaises (chaque fois que le type exact demandé ne figure pas dans le dictionnaire).

Questions connexes