2009-03-17 7 views
35

Je voudrais créer un objet Dictionary, avec des clés de type String, contenant des valeurs de type générique. J'imagine qu'il ressemblerait à quelque chose comme ceci:Puis-je créer un dictionnaire de types génériques?

Dictionary<string, List<T>> d = new Dictionary<string, List<T>>(); 

Et me permettre d'ajouter ce qui suit:

d.Add("Numbers", new List<int>()); 
d.Add("Letters", new List<string>()); 

Je sais que je peux le faire pour une liste de chaînes, par exemple, en utilisant cette syntaxe:

Dictionary<string, List<string>> d = new Dictionary<string, List<string>>(); 
d.Add("Key", new List<string>()); 

mais je voudrais le faire pour une liste générique si possible ...

2 questions puis:

  1. Est-il possible?
  2. Quelle est la syntaxe?

Un grand merci,
Jon

Répondre

24

EDIT: Maintenant, je l'ai relu la question ...

Vous ne pouvez pas faire cela, mais une collection personnalisée serait manipuler dans une certaine mesure. Vous souhaitez essentiellement avoir une méthode générique Add:

public void Add<T>(string key, List<T> list) 

(. La collection elle-même ne être générique - à moins que vous vouliez faire le type de clé générique)

Vous ne pourriez pas extrait valeurs de manière fortement typée, car le compilateur ne sait pas quel type vous avez utilisé pour une clé particulière. Si vous faites la clé le type lui-même, vous finissez avec une légèrement meilleure situation, mais qui n'est toujours pas pris en charge par les collections existantes. C'est la situation à laquelle ma réponse originale répondait.

EDIT: réponse originale, quand je ne l'avais pas tout à fait lire correctement la question, mais qui peut être instructif de toute façon ...

Non, vous ne pouvez pas faire un argument de type dépendre d'une autre, je suis peur. C'est juste une des choses que l'on pourrait vouloir exprimer dans un système de type générique mais que les contraintes de .NET ne permettent pas. Il y aura toujours de tels problèmes, et les concepteurs de .NET ont choisi de garder les génériques relativement simples.

Cependant, vous pouvez écrire une collection pour l'appliquer assez facilement. J'ai an example in a blog post qui ne garde qu'une seule valeur, mais il serait facile d'étendre cela pour utiliser une liste.

+0

Ah, je vois le problème maintenant - il n'y a aucun moyen de faire l'inférence de type sur tout ce que vous extrait de la collection. Je suppose que c'est la raison pour laquelle ce n'est pas inclus en tant que fonctionnalité. Merci! –

+0

La collection elle-même peut aussi être générique: utiliser un 'C où T: C' serait utile. – reinierpost

17

Un truc pareil devrait-il fonctionner?

public class GenericDictionary 
{ 
    private Dictionary<string, object> _dict = new Dictionary<string, object>(); 

    public void Add<T>(string key, T value) where T : class 
    { 
     _dict.Add(key, value); 
    } 

    public T GetValue<T>(string key) where T : class 
    { 
     return _dict[key] as T; 
    } 
} 

Fondamentalement, il enveloppe tout le casting dans les coulisses pour vous.

3

Je préfère cette façon de mettre les types génériques dans une collection:

interface IList 
{ 
    void Add (object item); 
} 

class MyList<T> : List<T>, IList 
{ 
    public void Add (object item) 
    { 
    base.Add ((T) item); // could put a type check here 
    } 
} 

class Program 
{ 
    static void Main (string [] args) 
    { 
    SortedDictionary<int, IList> 
     dict = new SortedDictionary<int, IList>(); 

    dict [0] = new MyList<int>(); 
    dict [1] = new MyList<float>(); 

    dict [0].Add (42); 
    dict [1].Add ("Hello"); // Fails! Type cast exception. 
    } 
} 

Mais vous ne perdez au moment de la compilation des contrôles de type.

+0

Exactement ce que je pensais: utilisez un Dictionary . –

2

Nous utilisons beaucoup de réflexion pour créer un outil d'administration extensible. Nous avions besoin d'un moyen d'enregistrer des éléments dans la recherche globale dans la définition du module. Chaque recherche renvoie les résultats de manière cohérente, mais chacun a des dépendances différentes. Voici un exemple de nous inscrire recherche d'un seul module:

public void ConfigureSearch(ISearchConfiguration config) 
    { 
     config.AddGlobalSearchCallback<IEmploymentDataContext>((query, ctx) => 
     { 
      return ctx.Positions.Where(p => p.Name.Contains(query)).ToList().Select(p => 
       new SearchResult("Positions", p.Name, p.ThumbnailUrl, 
        new UrlContext("edit", "position", new RouteValueDictionary(new { Id = p.Id })) 
        )); 
     }); 
    } 

Dans l'arrière-plan lors de l'inscription du module, nous parcourons chaque module et ajoutez le Func à un SearchTable avec une instance de:

public class GenericFuncCollection : IEnumerable<Tuple<Type, Type, Object>> 
{ 
    private List<Tuple<Type, Type, Object>> objects = new List<Tuple<Type, Type, Object>>(); 

    /// <summary> 
    /// Stores a list of Func of T where T is unknown at compile time. 
    /// </summary> 
    /// <typeparam name="T1">Type of T</typeparam> 
    /// <typeparam name="T2">Type of the Func</typeparam> 
    /// <param name="func">Instance of the Func</param> 
    public void Add<T1, T2>(Object func) 
    { 
     objects.Add(new Tuple<Type, Type, Object>(typeof(T1), typeof(T2), func)); 
    } 

    public IEnumerator<Tuple<Type, Type, object>> GetEnumerator() 
    { 
     return objects.GetEnumerator(); 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return objects.GetEnumerator(); 
    } 
} 

Puis quand nous l'appelons finalement, nous le faisons avec la réflexion:

var dependency = DependencyResolver.Current.GetService(search.Item1); 
var methodInfo = search.Item2.GetMethod("Invoke"); 
return (IEnumerable<SearchResult>)methodInfo.Invoke(search.Item3, new Object[] { query, dependency }); 
1

Autre possibilité d'utiliser la variable dynamique.

Par exemple:

Dictionary<string, List<dynamic>> d = new Dictionary<string, List<dynamic>>(); d.Add("Key", new List<dynamic>());

la dynamique variable de résoudre le type à l'exécution.

1

Je n'ai pas trouvé ce que je cherchais ici mais après avoir lu je pense que ce pourrait être ce qui est demandé pour essayer de répondre.

Le problème est que lorsque vous utilisez Dictionary, il s'agit d'un type construit fermé et tous les éléments doivent être du type TValue. Je vois cette question dans un certain nombre d'endroits sans une bonne réponse.

Le fait est que je veux indexer mais chaque élément doit avoir un type différent et basé sur la valeur de TKey nous connaissons déjà le type. Ne pas essayer de contourner la boxe, mais en essayant simplement d'obtenir un accès plus élégant quelque chose comme DataSetExtensions Field. Et ne pas vouloir utiliser dynamique parce que les types sont connus et ce n'est tout simplement pas voulu.

Une solution peut être de créer un type non générique qui n'expose pas T au niveau de la classe et donc provoque la construction de la partie TValue du dictionnaire à fermer. Puis saupoudrer dans une méthode fluide pour aider l'initialisation.

public class GenericObject 
{ 
    private object value; 

    public T GetValue<T>() 
    { 
     return (T)value; 
    } 

    public void SetValue<T>(T value) 
    { 
     this.value = value; 
    } 

    public GenericObject WithValue<T>(T value) 
    { 
     this.value = value; 
     return this; 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Dictionary<string, GenericObject> dict = new Dictionary<string, GenericObject>(); 

     dict["mystring"] = new GenericObject().WithValue<string>("Hello World"); 
     dict["myint"] = new GenericObject().WithValue<int>(1); 

     int i = dict["myint"].GetValue<int>(); 
     string s = dict["mystring"].GetValue<string>(); 
    } 
} 
0

L'un des moyens est de créer une valeur dictionnaire avec « objet » type comme:

Dictionary<string, object> d = new Dictionary<string, object>();

Donc, ici l'objet type de données est utilisé comme un type générique, vous pouvez mettre quelque chose dans ceci comme une valeur.

Questions connexes