2009-10-07 9 views
2

Je dois envoyer différents IEnumerables à un objet Printer. Cet objet d'imprimante va alors leur faire quelque chose, à l'intérieur d'une boucle foreach.énumérer à travers tous IEnumerables

class Printer 
{ 
    public Printer(IEnumerable list) 
    { 
     foreach (var enumerable in list) 
     { 
      //DO STUFF 
     } 
    } 
} 

Cela me permet d'envoyer toute dénombrable, comme un List<T> à l'objet de l'imprimante. tels que

var list = new List<string> {"myList"}; 
    new Printer(list); //mylist 

Cela fonctionne très bien. mais si j'envoie un Dictionary<T, T> comme:

var dictionary = new Dictionary<int, string> {{1, "mydict"}}; 
new Printer(dictionary); //[1, mydict] 

Il aura une clé et une valeur. Ce que je voudrais cependant, serait un accès séparé à la propriété Value à l'intérieur de la boucle foreach. Tout ce que j'ai accès à est l'objet énumérable, qui n'a pas de propriétés que je peux utiliser.

Maintenant, que faire si le type de données T est un objet contenant plusieurs propriétés (cela vaut pour les deux exemples). Comment pourrais-je utiliser ces propriétés dans ma boucle foreach?

Est-ce que je dois honnêtement créer une surcharge du constructeur, pour chaque type de données que je pourrais envoyer?

De plus, tout ce que j'ai besoin de faire dans foreach n'est pas fiable pour n'importe quel type de données - car il ne va pas tout manipuler. J'ai besoin d'ACCÈS à toutes les propriétés.

Aussi, ce n'est qu'un exemple de code, pas réellement le code de production que j'utilise dans mon application.

+0

N'auriez-vous pas le même problème dans le premier exemple? Après tout, vous pourriez obtenir une liste où T est un objet complexe contenant plusieurs propriétés? –

+0

Etes-vous sûr qu'il accepte IEnumerable et non IEnumerable ? –

+0

Oui, c'est ce que j'ai maladroitement tenté d'expliquer dans le paragraphe "Et maintenant?". Je vais essayer d'éditer mon post pour le rendre plus clair. – CasperT

Répondre

4

Pouvez-vous changer le code de la classe d'imprimante? S'il acceptait quelque chose comme un IEnumerable<IPrintable> au lieu d'un IEnumerable, ce serait plus facile. Avec une interface comme ceci:

interface IPrintable 
{ 
    void Print(); 
} 

tous les objets qui seraient envoyés au Printer aurait besoin de mettre en œuvre cette interface.Ensuite, vous pouvez faire:

class Printer 
{ 
    public Printer(IEnumerable<IPrintable> list) 
    { 
     foreach (var enumerable in list) 
     { 
      enumerable.Print(); 
     } 
    } 
} 

Et si vous avez un dictionnaire d'objets imprimables, quelque chose comme:

var dict = new Dictionary<int,IPrintable>(); 

Vous pouvez simplement transmettre les valeurs à la fonction:

var printer = new Printer(dict.Values); 
+0

Cela semble être une solution intelligente! Je vais laisser le fil vivre pendant quelques heures. Si personne ne l'emporte sur votre solution, j'accepterai votre réponse. – CasperT

2

Si vous voulez traiter différents types différemment, vous devriez probablement faire des méthodes différentes. Si vous voulez les traiter de la même manière, vous devez accepter une interface commune et utiliser uniquement les méthodes définies pour l'interface.

Il serait possible de faire

if (list is Dictionary<int, string>) { 
    // do special case 
} 

mais je frémis à la pensée.

Vous pouvez même vérifier génériquement:

class Printer<T> 
{ 
    public Printer<T>(IEnumerable list) 
    { 
     foreach (var enumerable in list) 
     { 
      if (list is Dictionary<T, T>) { 
       //DO STUFF 
      } 
     } 
    } 
} 
+0

Je frémis à cette pensée ainsi – CasperT

+0

Y at-il de toute façon de vérifier si la liste est un dictionnaire ? – CasperT

+1

Type de. Voir mon addition. – recursive

1

Le problème est qu'une collection, bien qu'il soit dénombrable, peut contenir différents types d'objets, comme vous avez vu la différence entre le List et le Dictionary. Pour éviter cela sans codage pour chaque type d'objet, vous devez accepter uniquement une collection énumérable d'un certain type que vous définissez, par exemple IEnumerable<IMyType>.

1

Si vous ne pouvez rien faire à l'appelé, c'est à l'appelant de s'assurer qu'il passe un IEnumerable qui est "valide" pour l'imprimante, comme passer dictionary.Values au lieu de dictionary dans votre exemple. Toutefois, si la classe est publique et sera utilisée par des utilisateurs tiers, il est préférable d'ajouter une contrainte générique à votre IEnumerable, comme d'autres l'ont indiqué.

3

Vous devez extraire un énumérable avec les valeurs que vous souhaitez transmettre avant d'appeler la nouvelle imprimante(). Dans le cas du dictionnaire, c'est simple: il suffit d'utiliser dict.Values. Un cas plus général est le suivant:

var list = List<MyObject>()... 

var printer = new Printer(list.Select(x => x.MyProperty)); 
4

Vous pouvez modifier votre méthode pour accepter un délégué qui renvoie les données dont la méthode d'impression a besoin. Quelque chose comme ceci:

// You will not need this class, if you always want a single string result. 
class PrinterData 
{ 
    public string Value { get; set; } 
    // More properties? 
} 
class Printer 
{ 
    public Printer<T>(IEnumerable<T> list, Func<T, PrinterData> func) 
    { 
     foreach (T item in list) 
     { 
      PrinterData data = func(item); 
      // Do something with the data. 
     } 
    } 
} 

Utilisation:

int[] ints = new int[] {1,2,3}; 
new Printer().Print(ints, x => new PrinterData() { Value = x.ToString() }); 

var dictionary = new Dictionary<int, string> {{1, "mydict"}}; 
new Printer().Print(dictionary, x => new PrinterData() { Value = x.Name + " = " + x.Value }); 

par la réponse de Erik Stendahl est très similaire.

0

Voici le résultat: J'ai utilisé votre aide les gars, donc je suppose que je ne devrais pas voter le mien comme réponse.

class Printer 
{ 
    public Printer(IEnumerable<IPrintable> list) //Accepts any collection with an object that implements IPrintable interface 
    { 
     foreach (var enumerable in list) //iterate through list of objects 
     { 
      foreach (var printable in enumerable)//loops through properties in current object 
      { 
       //DO STUFF 
      } 
     } 
    } 
} 

interface IPrintable : IEnumerable { } 
class SomeObject : IPrintable 
{ 
    public string Property1 { get; set; } 
    public string Property2 { get; set; } 

    public interface IEnumerable 
    { 
     IEnumerator GetEnumerator(); //Returns a Enumerator 
    } 

    public IEnumerator GetEnumerator() 
    { 
     yield return Property1; 
     yield return Property2; 
    } 
} 

Je naturellement besoin de mettre en œuvre sur mesure GetEnumerator() objet foreach - pas de problème si!

+0

N'hésitez pas à la mettre en doute si :) – CasperT

Questions connexes