2010-10-26 17 views
1

Je travaille dans le framework .NET 2.0. J'ai du code qui marche, je veux juste que ça fonctionne un peu plus élégamment.Obtenir l'inférence de type appropriée pour un type plus compliqué dans une méthode générique

J'ai un besoin de manière efficace « miroir » un objet Dictionary tel que si nous commençons par un objet comme celui-ci

Dictionary<TKey,TValue> StartDictionary; 

Nous pouvons en miroir comme ça

Dictionary<TValue,TKey> MirroredDictionary = MirrorDictionary(StartDictionary); 

Et nous finirions avec un nouveau dictionnaire avec les valeurs et les clés échangées pour chaque KeyValuePair

Avant que quelqu'un me demande pourquoi: le dictionnaire source est assez grand et chargé une fois de re appels de flection lorsque mon programme charge. Je ne veux pas lancer les mêmes appels de réflexion une seconde fois pour charger le dictionnaire en miroir. Créer un dictionnaire miroir et peupler ses valeurs et ses clés de la façon dont je suis venu me semblait beaucoup moins coûteux.

Alors étant le genre de personne qui déteste réécrire les choses, j'ai décidé d'écrire une méthode générique dans une classe d'aide que je dois faire le miroir en utilisant des génériques.

esprit Maintenant, vous j'ai écrit simples méthodes génériques avant pour les types scalaires normaux

Voici ce que je suis venu avec

public static TOutDic MirrorDictionary<TInDic, TOutDic>(TInDic InDictionary) 
    where TInDic : IDictionary 
    where TOutDic : IDictionary 
{ 
    Type[] KVPTypes = typeof(TInDic).GetGenericArguments(); 
    Type TKey = KVPTypes[0]; 
    Type TValue = KVPTypes[1]; 
    Type TDic = typeof(Dictionary<,>).MakeGenericType(TValue, TKey); 
    IDictionary OutDic = (IDictionary)Activator.CreateInstance(TDic); 
    foreach (DictionaryEntry DE in (IDictionary)InDictionary) OutDic.Add(DE.Value, DE.Key); 
    return (TOutDic)OutDic; 
} 

il un peu mais ça fonctionne, les charges les types de Clés et valeurs et crée une instance du dictionnaire en miroir

Ensuite, juste en faisant une boucle à travers les DictionaryEntries de base de l'InDictionary, il ajoute les éléments à l'OutDic et le retourne en le convertissant au Type attendu

Compile très bien

Maintenant, quand je vais l'appeler, je pense comme quand j'appelle une méthode générique pour un type scalaire je pourrais simplement en utilisant notre code snippits ci-dessus dire

Dictionary<TValue,TKey> MirroredDictionary = MirrorDictionary(StartDictionary); 

Mais ce ne compile pas me donne

Les arguments de type pour la méthode MirrorDictionary (TInDic) 'ne peuvent pas être déduits de l'utilisation. Essayez de spécifier explicitement les arguments de type.

Donc, si je l'appelle au lieu comme celui-ci

 Dictionary<TValue, TKey> MirrorDic = MirrorDictionary<Dictionary<Tkey, TValue>, Dictionary<TValue,TKey>>(StringDic); 

Il compile et fonctionne comme un charme.

Maintenant la question devient comment faire correctement déduire le type étant passé dans cette méthode lorsque le type étant transmis et le type étant transmis sont des types complexes comme dans cet exemple?

+0

Comment peut-il déduire TOutDic lorsque le paramètre d'entrée ne fournit que des informations de type TInDic? –

Répondre

1

Vous pouvez rendre la vie beaucoup plus facile pour le compilateur en lui indiquant les principaux types et la valeur ainsi:

public static Dictionary<TValue, TKey> MirrorDictionary<TKey, TValue> 
    (Dictionary<TKey, TValue> source) 
{ 
    Dictionary<TValue, TKey> destination = new Dictionary<TValue, TKey>(); 

    foreach (KeyValuePair<TKey, TValue> kvp in source) 
    { 
     destination.Add(kvp.Value, kvp.Key); 
    } 

    return destination; 
} 

Je ne pense pas que vous avez besoin de réflexion tout ici.

Exemple d'utilisation:

static void Main(string[] args) 
{ 
    Dictionary<int, string> source = new Dictionary<int, string>(); 
    source.Add(3, "foo"); 
    source.Add(4, "bar"); 

    DumpDic(source); 

    DumpDic(MirrorDictionary(source)); 

    Console.ReadLine(); 

} 

DumpDic est:

public static void DumpDic<TK, TV>(Dictionary<TK, TV> dic) 
{ 
    foreach (KeyValuePair<TK, TV> keyValuePair in dic) 
    { 
     Console.WriteLine("{0} => {1}", keyValuePair.Key, keyValuePair.Value); 
    } 
} 
+0

J'ai essayé d'abord de ne pas compiler pour moi pour une raison quelconque Laissez-moi revenir en arrière et réessayer, peut-être que j'ai foiré quand j'ai créé la méthode la première fois. Je republierai quand j'ai testé – TofuBug

+0

Yep complètement précipité sur la déclaration de la méthode et j'ai oublié de déclarer mes types si j'avais dictionnaire statique public MirrorDictionary (Dictionnaire InDictionary) quand j'avais besoin de public static Dictionary MirrorDictionary (Dictionnaire InDictionary) – TofuBug

0

Vous pouvez définir le dictionnaire Out comme un paramètre out. L'inférence de type ne ressemble pas au type de variable que vous affectez, mais uniquement aux types de paramètres. C'est la raison pour laquelle cela ne compile pas.

0

Vous devez indiquer ce que TValue et TKey sont. À moins qu'ils ne soient définis dans la signature de la méthode appelant ce code, ils n'ont aucun type spécifique. Vous devez donner quelque chose comme:

Dictionary<string, int> MirroredDictionary = MirrorDictionary(StartDictionary); 
+0

Cela n'aide pas le compilateur à déduire le type de TOutDic, et cette ligne donne la même erreur de compilation. – stevemegson

+0

@stevemegson - Non, en lisant plus loin, il n'est même pas proche, j'ai été tenté de le supprimer, mais après le downvote je voulais le laisser à quelqu'un pour commenter. –

+0

Je vais rétracter le downvote alors, je me suis seulement soucié de downvote parce que le single upvote en a fait la meilleure réponse à l'époque. – stevemegson

1

est ici une solution 3.5 (vous pouvez également l'utiliser dans la version 2.0 avec VS2008 et LinqBridge)

IDictionary<TValue, TKey> MirrorDictionary<TKey, TValue>(IDictionary<TKey, TValue> dict) 
{ 
    return dict.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); 
} 

Et une solution 2.0 pur

IDictionary<TValue, TKey> MirrorDictionary<TKey, TValue>(IDictionary<TKey, TValue> dict) 
{ 
    Dictionary<TValue, TKey> newDict = new Dictionary<TValue, TKey>(); 
    foreach(KeyValuePair<TKey, TValue> kvp in dict) 
    { 
     newDict.Add(kvp.Value, kvp.Key); 
    } 
    return newDict; 
} 

L'inférence de type devrait fonctionner correctement avec les deux solutions (car elles ont la même signature)

+0

En plus d'éviter la réflexion, cela implique également que les types d'entrée et de sortie sont corrects. Il n'y a rien pour vous empêcher d'appeler la méthode d'origine comme 'MirrorDictionary , Dictionary > (entrée)', et il compilera bien mais explosera avec un cast invalide à l'exécution. – stevemegson