Conversione di una matrice di tipo T in una matrice di tipo I in cui T implementa I in C #
-
06-07-2019 - |
Domanda
Sto cercando di realizzare qualcosa in C # che faccio facilmente in Java. Ma ho qualche problema. Ho un numero indefinito di matrici di oggetti di tipo T. A implementa un'interfaccia I. Ho bisogno di un array di I alla fine che è la somma di tutti i valori di tutti gli array. Supponiamo che nessun array contenga gli stessi valori.
Questo codice Java funziona.
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()]);
Tuttavia questo codice C # non lo fa:
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();
Quindi è ovvio che devo in qualche modo inserire l'array di T[]
in un IEnumerable<I>
per aggiungerlo all'elenco ma non sono sicuro del modo migliore per farlo? Qualche suggerimento?
EDIT: in via di sviluppo in VS 2008 ma deve essere compilato per .NET 2.0.
Soluzione
Il problema qui è che C # non supporta co-varianza (almeno non fino a C # 4.0, credo) in generici, quindi le conversioni implicite di tipi generici non funzioneranno.
Puoi provare questo:
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
{
list.AddRange(Array.ConvertAll<T, I>(arrayOfA, t => (I)t));
}
return list.ToArray();
Per tutti coloro che si imbattono in questa domanda e usano .NET 3.5, questo è un modo leggermente più compatto di fare la stessa cosa, usando Linq.
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
{
list.AddRange(arrayOfA.Cast<I>());
}
return list.ToArray();
Altri suggerimenti
Modificato per 2.0; può diventare:
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();
}
Che ne dici (in .NET 3.5):
I[] arr = src.SelectMany(x => x).Cast<I>().ToArray();
Per mostrarlo nel contesto:
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) };
}
}
Suppongo che tu abbia provato
list.AddRange((I[])arrayOfA);
già?
MODIFICA In risposta al tuo commento dicendo che il mio suggerimento non avrebbe funzionato: ho eseguito correttamente questo codice solo un minuto fa:
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") };
}
}
}
O ho appena fatto un fabbisogno?
Prova ad aggiungere un vincolo generico
dove T: I
Immagino che il problema qui sia che i generici non si rendono conto che T implementa I. Potrebbe funzionare per dichiarare esplicitamente T: I.
Inoltre potresti fare un ciclo for e aggiungere i tuoi oggetti T uno alla volta invece di usare AddRange.
La struttura dei tipi di c # non supporta attualmente questo (trattando Foo<T>
come Foo<I>
se X: I) questo è chiamato covarianza su I.
Lo fa il framework sottostante, e c # 4.0 è aggiungendo supporto
Poiché sono richiesti cast espliciti, la risposta di Marc è la più semplice.
Nel tuo codice C # non stai aggiungendo arrayOfA all'elenco dei risultati:
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
list.AddRange(arrayOfA);
return list.ToArray();
Tuttavia, se si utilizza .NET 3.5, è possibile farlo con LINQ:
return (from arrayOfA in arrays
from element in arrayOfA
select element as I).ToArray();
O usando i metodi LINQ:
return arrays.SelectMany(arrayOfA => arrayOfA.Cast<I>()).ToArray();
Le matrici di oggetti di riferimento sono covarianti in C # (lo stesso vale per Java).
Dal nome, immagino che la tua T sia un tipo generico e non reale, quindi devi limitarlo a un tipo di riferimento per ottenere la conversione implicita da T [] a I [].
Prova questo:
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();
}