2009-06-17 7 views
4

J'essaye d'accomplir quelque chose en C# que je fais facilement en Java. Mais ayant des problèmes. J'ai un nombre indéfini de tableaux d'objets de type T. A implémente une interface I. J'ai besoin d'un tableau de I à la fin qui est la somme de toutes les valeurs de tous les tableaux. Supposons qu'aucun tableau ne contienne les mêmes valeurs.Conversion d'un tableau de type T en un tableau de type I où T implémente I dans C#

Ce code Java fonctionne.

ArrayList<I> list = new ArrayList<I>(); 
for (Iterator<T[]> iterator = arrays.iterator(); iterator.hasNext();) { 
    T[] arrayOfA = iterator.next(); 
    //Works like a charm 
    list.addAll(Arrays.asList(arrayOfA)); 
} 

return list.toArray(new T[list.size()]); 

Toutefois, ce code C# ne compte pas:

List<I> list = new List<I>(); 
foreach (T[] arrayOfA in arrays) 
{ 
    //Problem with this 
    list.AddRange(new List<T>(arrayOfA)); 
    //Also doesn't work 
    list.AddRange(new List<I>(arrayOfA)); 
} 
return list.ToArray(); 

Il est donc évident que je dois obtenir en quelque sorte le tableau de T[] dans un IEnumerable<I> d'ajouter à la liste, mais je ne suis pas sûr que le meilleur façon de le faire? Aucune suggestion?

EDIT: Développement dans VS 2008 mais doit être compilé pour .NET 2.0.

+0

Quelle erreur obtenez-vous? –

+0

Argument invalide dans les deux cas. nouvelle liste (arrayOfA) - ne list.AddRange (nouvelle liste (arrayOfA)) - (mise à jour échoue –

+0

re commentaire/2.0) –

Répondre

5

Le problème ici est que C# ne supporte pas co-variance (du moins pas jusqu'à C# 4.0, je pense) dans les génériques, donc les conversions implicites de types génériques ne fonctionneront pas.

Vous pouvez essayer ceci:

List<I> list = new List<I>(); 
foreach (T[] arrayOfA in arrays) 
{ 
    list.AddRange(Array.ConvertAll<T, I>(arrayOfA, t => (I)t)); 
} 
return list.ToArray(); 

Pour tous ceux qui strumbles sur cette question et utilise .NET 3.5, c'est une façon un peu plus compact de faire la même chose, en utilisant Linq.

List<I> list = new List<I>(); 
foreach (T[] arrayOfA in arrays) 
{ 
    list.AddRange(arrayOfA.Cast<I>()); 
} 
return list.ToArray(); 
+0

Comme ça, c'est très élégant. J'ai dû donner quelques +1 mais j'ai fini par utiliser ça. Merci. Toujours confus à propos de la réponse de Throsten. Je ne sais pas pourquoi cela ne fonctionnera pas dans mon exemple, mais son code fonctionne bien? –

+0

Adrian: peut-être que votre T est un type de valeur? La covariance de type tableau fonctionne uniquement sur les types de référence. Dans votre exemple, essayez de lancer comme ceci: (IEnum ) (I []) arrayOfA - cela fonctionne-t-il? –

+0

Aha! Bingo. T est un type de valeur, j'imagine que j'aurais dû le spécifier à l'avance. Apprendre beaucoup de nouvelles choses aujourd'hui. Malheureusement, bien que votre suggestion n'a pas aidé, toujours coller avec la réponse ci-dessus. –

1

Essayez d'ajouter une contrainte générique

où T: Je

1

Je devine que le problème ici est que les médicaments génériques ne se rendent pas compte que T implémente I. Il pourrait fonctionner de déclarer explicitement T: I.

Vous pouvez également faire une boucle for et ajouter vos objets T un par un au lieu d'utiliser AddRange.

7

Edité pour 2.0; il peut devenir:

static void Main() { 
    IEnumerable<Foo[]> source = GetUndefinedNumberOfArraysOfObjectsOfTypeT(); 
    List<IFoo> list = new List<IFoo>(); 
    foreach (Foo[] foos in source) { 
     foreach (IFoo foo in foos) { 
      list.Add(foo); 
     } 
    } 
    IFoo[] arr = list.ToArray(); 
} 

Que diriez-vous (en .NET 3.5):

I[] arr = src.SelectMany(x => x).Cast<I>().ToArray(); 

Pour montrer cela dans le contexte:

using System.Collections.Generic; 
using System.Linq; 
using System; 
interface IFoo { } 
class Foo : IFoo { // A implements an interface I 
    readonly int value; 
    public Foo(int value) { this.value = value; } 
    public override string ToString() { return value.ToString(); } 
} 
static class Program { 
    static void Main() { 
     // I have an undefined number of arrays of objects of type T 
     IEnumerable<Foo[]> source=GetUndefinedNumberOfArraysOfObjectsOfTypeT(); 
     // I need an array of I at the end that is the sum of 
     // all values from all the arrays. 
     IFoo[] arr = source.SelectMany(x => x).Cast<IFoo>().ToArray(); 
     foreach (IFoo foo in arr) { 
      Console.WriteLine(foo); 
     } 
    } 
    static IEnumerable<Foo[]> GetUndefinedNumberOfArraysOfObjectsOfTypeT() { 
     yield return new[] { new Foo(1), new Foo(2), new Foo(3) }; 
     yield return new[] { new Foo(4), new Foo(5) }; 
    } 
} 
+0

Par curiosité, quel est le but de la SelectMany là-bas? Vous me battez par secondes sur le .Cast, donc +1 de moi :-) –

+0

Le SelectMany concatène efficacement les éléments des tableaux. –

+0

Awesomecake, je pense que je vais ajouter celui à la liste des choses cool que je ne savais pas à propos de LINQ. Ta fellas –

3

Je suppose que vous avez essayé

list.AddRange((I[])arrayOfA); 

déjà?

EDIT En réponse à votre commentaire disant que ma suggestion ne fonctionnerait pas: j'ai couru avec succès ce code il y a à peine une minute:

using System; 
using System.Collections.Generic; 

namespace Tester 
{ 
    class Program 
    { 
     private interface I 
     { 
      string GetValue(); 
     } 

     private class A : I 
     { 
      private string value; 

      public A(string v) 
      { 
       value = v; 
      } 

      public string GetValue() 
      { 
       return value; 
      } 
     } 

     static void Main(string[] args) 
     { 
      List<I> theIList = new List<I>(); 

      foreach (A[] a in GetAList()) 
      { 
       theIList.AddRange((I[])a); 
      } 

      foreach (I i in theIList) 
      { 
       Console.WriteLine(i.GetValue()); 
      } 
     } 

     private static IEnumerable<A[]> GetAList() 
     { 
      yield return new [] { new A("1"), new A("2"), new A("3") }; 
      yield return new [] { new A("4") }; 
     } 
    } 
} 

Ou je ne muss juste une exigence?

+1

:) Oui cela ne marchera dans aucune langue –

+0

Je ne vois pas pourquoi ce qui précède ne devrait pas fonctionner. J'ai édité ma réponse et posté un exemple de code qui fonctionne. –

+0

Non, je ne vois pas comment cela fonctionne car il ne fonctionne certainement pas sur mon exemple et pourtant votre code compile et fonctionne bien. Pour autant que je comprenne, vous ne pouvez jamais lancer de T [] à I [] même si T: I. Excusez-moi de douter, je dois m'excuser! –

1

La structure de type de C# ne supporte pas ce (le traitement de la Foo<T> comme Foo<I> si X: I) ce que l'on appelle covariance sur I.

Le cadre sous-jacent fait, et c# 4.0 is adding support for it

Comme de telles distributions explicites sont requises, la réponse de Marc est la plus simple.

0

Dans votre code C# vous n'êtes pas ajoutez le arrayOfA à la liste des résultats:

List<I> list = new List<I>(); 
foreach (T[] arrayOfA in arrays) 
    list.AddRange(arrayOfA); 

return list.ToArray(); 

Cependant, si vous utilisez .NET 3.5, vous pouvez le faire avec LINQ:

return (from arrayOfA in arrays 
     from element in arrayOfA 
     select element as I).ToArray(); 

Ou utilisant les méthodes LINQ:

return arrays.SelectMany(arrayOfA => arrayOfA.Cast<I>()).ToArray(); 
+0

Oui, bien repéré. Je pense que tout le monde a compris l'essentiel de la question. Fixe et un +1 pour vous pour les yeux tranchants :) (Pas de LINQ donc impossible d'utiliser votre solution.) –

+0

Le code non-LINQ devrait fonctionner pour vous, néanmoins, comme le dit Rasmus, les tableaux d'objets de référence sont covariants en C#. –

+0

Je suppose que le code non-LINQ ne fonctionne pas pour la même raison que je ne peux pas transtyper T [] en I []. À ce stade, il semble que c'est parce que T est en fait un type de valeur. BAck à l'école pour moi! –

0

Arrays of reference objects are covariant in C# (la même chose est vraie pour Java). Du nom, je suppose que votre T est un type générique et non réel, donc vous devez le restreindre à un type de référence afin d'obtenir la conversion implicite de T [] en I [].

Essayez ceci:

public static I[] MergeArrays<T,I>(IEnumerable<T[]> arrays) 
    where T:class,I 
{ 
    List<I> list = new List<I>(); 
    foreach(T[] array in arrays){ 
     list.AddRange(array); 
    } 
    return list.ToArray(); 
} 
Questions connexes