Convertir una matriz de tipo T en una matriz de tipo I donde T implementa I en C #
-
06-07-2019 - |
Pregunta
Estoy tratando de lograr algo en C # que hago fácilmente en Java. Pero teniendo algunos problemas. Tengo un número indefinido de matrices de objetos del tipo T. A implementa una interfaz I. Necesito una matriz de I al final que sea la suma de todos los valores de todas las matrices. Suponga que ninguna matriz contendrá los mismos valores.
Este código Java funciona.
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()]);
Sin embargo, este código C # no:
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();
Entonces, es obvio que necesito obtener de alguna manera la matriz de T[]
en un IEnumerable<I>
para agregar a la lista, pero no estoy seguro de la mejor manera de hacerlo. ¿Alguna sugerencia?
EDITAR: Desarrollado en VS 2008 pero necesita compilar para .NET 2.0.
Solución
El problema aquí es que C # no admite covarianza (al menos no hasta C # 4.0, creo) en genéricos para que las conversiones implícitas de tipos genéricos no funcionen.
Puedes probar esto:
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
{
list.AddRange(Array.ConvertAll<T, I>(arrayOfA, t => (I)t));
}
return list.ToArray();
Para cualquiera que se meta en esta pregunta y esté usando .NET 3.5, esta es una forma un poco más compacta de hacer lo mismo, usando Linq.
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
{
list.AddRange(arrayOfA.Cast<I>());
}
return list.ToArray();
Otros consejos
Editado para 2.0; puede convertirse en:
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();
}
¿Qué tal (en .NET 3.5):
I[] arr = src.SelectMany(x => x).Cast<I>().ToArray();
Para mostrar esto en contexto:
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) };
}
}
Supongo que lo has intentado
list.AddRange((I[])arrayOfA);
ya?
EDITAR en respuesta a su comentario diciendo que mi sugerencia no funcionaría: Ejecuté este código con éxito hace solo un minuto:
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 acabo de pensar en un requisito?
Intente agregar una restricción genérica
donde T: I
Supongo que el problema aquí es que los genéricos no se dan cuenta de que T implementa I. Podría funcionar declarar explícitamente T: I.
También podría hacer un bucle for y agregar sus objetos T uno a la vez en lugar de usar AddRange.
La estructura de tipos de c # actualmente no es compatible con esto (tratando Foo<T>
como Foo<I>
si X: I) esto se denomina covarianza en I.
El marco subyacente sí, y c # 4.0 es agregando soporte para ello
Como se requieren tales conversiones explícitas, la respuesta de Marc es la más simple.
En su código C # no está agregando el arrayOfA a la lista de resultados:
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
list.AddRange(arrayOfA);
return list.ToArray();
Sin embargo, si está utilizando .NET 3.5 puede hacerlo con LINQ:
return (from arrayOfA in arrays
from element in arrayOfA
select element as I).ToArray();
O usando métodos LINQ:
return arrays.SelectMany(arrayOfA => arrayOfA.Cast<I>()).ToArray();
Las matrices de objetos de referencia son covariantes en C # (lo mismo es cierto para Java).
Por el nombre, supongo que su T es un tipo genérico y no real, por lo que debe restringirlo a un tipo de referencia para obtener la conversión implícita de T [] a I [].
Prueba esto:
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();
}