Conversion d'un tableau de type T en tableau de type I où T implémente I en C #
-
06-07-2019 - |
Question
J'essaie d'accomplir quelque chose en C # que je fais facilement en Java. Mais avoir des problèmes. J'ai un nombre indéterminé de tableaux d'objets de type T. A implémente une interface I. J'ai besoin d'un tableau de I à la fin qui représente la somme de toutes les valeurs de tous les tableaux. Supposons qu'aucun tableau ne contiendra les mêmes valeurs.
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()]);
Cependant, ce code C # ne le fait 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 intégrer le tableau de T[]
dans un IEnumerable<I>
pour l’ajouter à la liste, mais je ne suis pas sûr de la meilleure façon de procéder. Des suggestions?
EDIT: Développé dans VS 2008 mais doit être compilé pour .NET 2.0.
La solution
Le problème ici est que C # ne prend pas en charge co-variance (du moins pas avant le C 4.0, je pense) dans les génériques afin que les conversions implicites de types génériques ne fonctionnent 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 ceux qui ont des problèmes avec cette question et qui utilisent .NET 3.5, c'est une façon un peu plus compacte de faire la même chose, avec Linq.
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
{
list.AddRange(arrayOfA.Cast<I>());
}
return list.ToArray();
Autres conseils
Edité pour 2.0; cela 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 (dans .NET 3.5):
I[] arr = src.SelectMany(x => x).Cast<I>().ToArray();
Pour afficher ceci en 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) };
}
}
Je suppose que vous avez essayé
list.AddRange((I[])arrayOfA);
déjà?
MODIFIER En réponse à votre commentaire, disant que ma suggestion ne fonctionnerait pas: j'ai réussi à exécuter ce code il y a une minute à peine:
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 est-ce que je viens juste de répondre à une exigence?
Essayez d'ajouter une contrainte générique
où T: I
Je suppose que le problème ici est que les génériques ne réalisent pas que T implémente I. Cela peut fonctionner de déclarer explicitement T: I.
Vous pouvez également créer une boucle for et ajouter vos objets T un par un au lieu d'utiliser AddRange.
La structure de type de c # ne supporte pas cela actuellement (en considérant le Foo<T>
comme un Foo<I>
si X: I), on parle de covariance sur I.
La structure sous-jacente existe, et c # 4.0 est ajout de la prise en charge
Comme de tels conversions explicites sont nécessaires, la réponse de Marc étant la plus simple.
Dans votre code C #, vous n'ajoutez pas arrayOfA à la liste des résultats:
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
list.AddRange(arrayOfA);
return list.ToArray();
Toutefois, 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 en utilisant les méthodes LINQ:
return arrays.SelectMany(arrayOfA => arrayOfA.Cast<I>()).ToArray();
Les tableaux d'objets de référence sont covariants en C # (il en va de même pour Java).
D'après son nom, je suppose que votre T est un type générique et non réel. Vous devez donc le limiter à 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();
}