2009-03-19 4 views
2

Nécessité d'avoir un de type sécurité sac d'articles qui mettent tous en œuvre une interface générique.Comment est-ce que je peux faire un type coffre-fort d'articles qui implémentent tous une interface générique?

Le désir est de faire quelque chose comme:

var stringItem = new IItem<string>(); 
var numberItem = new IItem<int>(); 
var items = new List<IItem<T>>(); //T of course doesn't accomplish what I want 

items.Add(stringItem); 
items.Add(numberItem); 

Quelque chose comme:

interface IItem 
{ 
    object Value { get; set; } 
} 

//Update: 2009-03-19 03:08 PM MST 
//Added the following interface for clarity of my question 

interface IItem<T> : IItem 
{ 
    new T Value { get; set; } 
} 

Ensuite, je pouvais:

var items = new List<IItem>(); 

Mais, je perds la sécurité de type dans mon sac . Donc, je pensais Dictionary:

var dict = new Dictionary<Type, List<IItem<T>>>(); //T is wrong again 

dict.Add(typeof(string), new List<IItem<string>>); //that sure looks nice 
+0

Je sais qu'une solution plus élégante existe. Après tout, les génériques eux-mêmes ont été ajoutés à .NET. Peut-être que la dynamique dans C# 4.0 nous donnera un chemin plus clair vers cette réponse. Je suppose que nous ne sommes tout simplement pas assez intelligents pour comprendre la réponse de type sécurité aujourd'hui. :) –

+0

Il pourrait être utile si vous avez décrit ce que vous avez l'intention de faire avec votre sac mélangé. –

+0

Je vais avec l'héritage de l'interface générique object-vs-type-safe-generic. Pour mon sac, j'ai ajouté une requête LINQ soignée pour obtenir un sous-ensemble d'éléments spécifiques au type: public IEnumerable > GetItems () {Type t = typeof (T); return (IEnumerable >) Items.Where (i => i.Value.GetType() == t); } –

Répondre

5

Je ne pense pas que vous pouvez échapper au fait que sont différents IItem<int> et IItem<string>; l'approche habituelle est une base interface:

interface IItem { 
    object Value {get;} 
} 
interface IItem<T> : IItem { 
    new T Value {get;} 
} 

De cette façon, vous codez contre IItem, mais les cas réels (qui mettent généralement en œuvre IItem<T> pour certains T) sont stll fortement typé en interne.

+0

Dans les tampons de protocole, je fais quelque chose de similaire, mais éviter de réutiliser les noms - J'ai "WeakFoo()" dans le type faible, et "Foo()" dans le type fort. Ceci aide particulièrement quand WeakFoo() renvoie un IItem et Foo() renvoie un IItem . –

+0

J'ai écrit l'interface de base dans ma question. :) –

+0

@ericis - IMO, la chose clé est le fait que IItem : IItem; si vous aviez déjà cela, alors bien - mais il n'a pas été déclaré AFAIK –

1

Découvrez la mise en œuvre PolyDictionary here.

class Key<T> { public Key() { } } 

class PolyDictionary { 
    private Dictionary<object, object> _table; 

    public PolyDictionary() { 
     _table = new Dictionary<object, object>(); 
    } 

    public void Add<T>(Key<T> key, T value) { 
     _table.Add(key, value); 
    } 

    public bool Contains<T>(Key<T> key) { 
     return _table.ContainsKey(key); 
    } 

    public void Remove<T>(Key<T> key) { 
     _table.Remove(key); 
    } 

    public bool TryGetValue<T>(Key<T> key, out T value) { 
     object objValue; 
     if (_table.TryGetValue(key, out objValue)) { 
      value = (T)objValue; 
      return true; 
     } 
     value = default(T); 
     return false; 
    } 

    public T Get<T>(Key<T> key) { 
     T value; 
     if (!TryGetValue(key, out value)) 
      throw new KeyNotFoundException(); 
     return value; 
    } 

    public void Set<T>(Key<T> key, T value) { 
     _table[key] = value; 
    } 
} 
+0

pas intrinsèquement de type sécurisé - quel est le but. –

+0

Vous ne pouvez pas éviter de répondre à votre problème, il doit être quelque part. Vous pourriez avoir PolyDictionary cast NodeBase au Noeud au lieu de l'objet à T, mais est-ce que cela améliore quelque chose? –

+0

En outre, ce PolyDictionary * est * sécurisé. Il stocke les données en tant qu'objet en interne, et alors? En fin de compte, les données sont stockées dans des cellules de mémoire non-typées comme un morceau d'octets :) –

1

Vous devriez être en mesure de sortir avec faire quelque chose comme ce qui suit

interface IItem { object Value {get; set;}} 

interface IItem<T> : IItem { T Value {get; set;}} 

var items = new List<IItem>(); 

items.add(new IItem<string>()); 
items.add(new IItem<int>()); 

Mais vous aurez encore à faire une coulée lorsque vous sortez.

+0

J'ai écrit l'interface de base dans ma question. :) Oh, et je crois que IItem aurait besoin d'hériter de IItem et remplacer la valeur ... –

+0

Correction de l'exemple de code, le IItem devait hériter de IItem – Gregg

0

Créez une nouvelle classe qui a pour seul but de mettre en boîte les valeurs qui vous intéressent. - Cette classe doit accepter soit un StringItem, soit un NumberItem. - La classe doit vous permettre d'accéder à la chaîne ou au membre de l'élément de nombre. Lorsque vous ajoutez vos objets à la liste, vous devez placer vos objets dans la boîte, puis les décocher lorsque vous les retirez. Si cela était en C++, alors le surcoût de ce design ne devrait pas être plus qu'un déréférencement de pointeur supplémentaire, en fonction de la façon dont vous l'implémentez, mais les expressions clés ici sont "boxing" et "unboxing".

0
public class GrabBag<T> 
{ 
    private ArrayList inner = new ArrayList(); 

    public void Add(T item) 
    { 
     inner.Add(item); 
    } 

    public IEnumerable<U> Get<U>() where U : T 
    { 
     return inner.OfType<U>(); 
    } 
} 

Ou, vous pouvez simplement utiliser la liste générique et appeler OfType. Cela a plus de sens.