2010-06-28 4 views
3

Mon petit esprit ne peut pas trouver une solution élégante à ce problème. Supposons que je classe comme celle-ci:Solution générique aux classes simples + usine

public class Foo<T> 
    { 
     public RecordType Type { get; set; } 
     public T Value { get; set; } 
    } 

RecordType peut ressembler à ceci:

public enum RecordType 
    { 
     EmptyRecord, 
     BooleanRecord, 
     IntegerRecord, 
     StringRecord, 
     ByteRecord 
    } 

L'objectif est de traiter une IEnumerable<Foo<T>> uniforme pour une itération et/ou pour allumer le RecordType et effectuer une action en évitant de mettre en boîte les types intrinsèques si possible. En outre, il serait bon d'utiliser une usine pour créer ces Foo d'une méthode d'usine.

J'ai fouillé avec quelques implémentations rapides de la communalité dans la classe de base ou l'interface et rien de ce que j'ai trouvé avec répondu à ce problème, apparemment, très simple avec élégance.

Petite modification: J'aurais dû mentionner que mon objectif principal est d'utiliser le .Value sans forcer un casting sur l'appelant.

+1

Est-ce que T est corrélé au RecordType de quelque façon que ce soit? – Grzenio

+0

Bonne question, oui, supposons que T est booléen, int, string, byte [] respectivement (pour cet exemple) – Marc

Répondre

2

Vous pouvez introduire une IFoo interface non générique:

public interface IFoo 
{ 
    RecordType Type { get; set; } 
} 

qui est mis en œuvre par la classe Foo générique:

public class Foo<T> : IFoo 
{ 
    public T Value { get; set; } 
} 

et créer une méthode de fabrication qui crée une instance Foo selon RecordType :

public static IFoo CreateFoo(RecordType type) 
{ 
    switch (type) 
    { 
     case RecordType.Bool: return new Foo<bool>(); 
     // ... 
    } 
} 

Une fois que vous avez créé une instance Foo de cette façon, vous ne pouvez pas accéder à la valeur pour le moment, car vous ne connaissez pas encore le paramètre type. Mais vous pouvez vérifier le type en utilisant la propriété de type et faire la distribution appropriée:

IFoo foo = CreateFoo(RecordType.Bool); 

if (foo.Type == RecordType.Bool) 
{ 
    Foo<bool> boolFoo = (Foo<bool>)foo; 
    bool value = boolFoo.Value; 
} 

Si vous avez une méthode qui fonctionne avec des objets Foo, par exemple:

void DoIt<T>(IEnumerable<Foo<T>> foos) 
{ 
    foreach (Foo<T> foo in foos) 
    { 
     Qux(foo.Value); 
    } 
} 

et ont une dénombrable de IFoo objets, vous pouvez lancer/OfType que:

IEnumerable<IFoo> foos = // ... 
DoIt<bool>(foos.OfType<Foo<bool>>()); 

donc, essentiellement, vous utilisez Foo <T> quand vous savez T à la compilation, et IFoo si vous connaissez T seulement à l'exécution. IFoo nécessite une vérification pour le transformer en Foo <T> pour certains T au moment de l'exécution.

+0

Merci pour la réponse. Je suis venu avec une variation de ceci plus tôt et ne pourrait pas abstraire loin obligeant les 'IFoo' dans' Foo 'pour un appelant non plus. A tout moment que j'ai créé, en usine, une collection de 'IFoo', je demande à l'appelant soit de connaître T et obtenir un sous-ensemble ou dans le corps d'itération basculer sur Type pour lancer dans un Foo . Je pense que je veux pousser ce langage statique trop loin. Merci d'avoir confirmé ma suspicion! Peut-être que j'aurais dû demander à l'origine, quelle est la meilleure façon de le faire. =) – Marc