A conversão de uma matriz do tipo T para uma matriz de tipo I onde T implementos I em C #
-
06-07-2019 - |
Pergunta
Eu estou tentando realizar algo em C # que eu faço facilmente em Java. Mas tendo alguns problemas. I ter um número indefinido de matrizes de objectos do tipo T. A implementa uma interface I. Preciso de um conjunto de I na extremidade que é a soma de todos os valores de todas as matrizes. Suponha que há matrizes irá conter os mesmos valores.
Este obras de código Java.
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()]);
No entanto, este código C # não:
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();
Por isso é que eu óbvia necessidade de alguma forma obter a matriz de T[]
em um IEnumerable<I>
para adicionar à lista, mas eu não tenho certeza que a melhor maneira de fazer isso? Alguma sugestão?
EDIT: Desenvolver no VS 2008, mas necessidades para compilar for .NET 2.0.
Solução
A questão aqui é que C # não suporta co-variância (pelo menos não até C # 4.0, eu acho) em genéricos tão conversões implícitas de tipos genéricos não vai funcionar.
Você poderia tentar o seguinte:
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 qualquer um que strumbles em toda esta questão e está usando .NET 3.5, esta é uma maneira um pouco mais compacto de fazer a mesma coisa, usando Linq.
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
{
list.AddRange(arrayOfA.Cast<I>());
}
return list.ToArray();
Outras dicas
Editado para 2,0; pode tornar-se:
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();
}
Como sobre (em .NET 3.5):
I[] arr = src.SelectMany(x => x).Cast<I>().ToArray();
Para mostrar isso em 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) };
}
}
Eu suponho que você já tentou
list.AddRange((I[])arrayOfA);
já?
Editar Em resposta ao seu comentário dizendo que a minha sugestão não funcionaria: Corri com sucesso este código apenas um minuto atrás:
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 será que eu só muss um requisito?
Tente adicionar uma restrição genérica
onde T: I
Eu estou supondo que o problema aqui é que os genéricos não percebe que t alfaias I. Pode funcionar para declarar explicitamente T: I.
Além disso, você pode fazer um loop e adicionar seu T objetos um de cada vez em vez de usar AddRange.
A estrutura tipo de c # não suporta atualmente isso (tratar a Foo<T>
como um Foo<I>
se X: I). Isto é chamado de covariância em I
A estrutura subjacente faz, e c # 4.0 é adicionando suporte para ele
Como são necessários tais conversões explícitas, a resposta de Marc sendo o mais simples.
Em seu código C # você não está adicionando a arrayOfA à lista de resultados:
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
list.AddRange(arrayOfA);
return list.ToArray();
No entanto, se você estiver usando o .NET 3.5 você pode fazer isso com o LINQ:
return (from arrayOfA in arrays
from element in arrayOfA
select element as I).ToArray();
Ou usando métodos LINQ:
return arrays.SelectMany(arrayOfA => arrayOfA.Cast<I>()).ToArray();
Arrays de objetos de referência são covariant em C # (o mesmo é verdadeiro para Java).
A partir do nome, acho que o T é um genérico e não um tipo real, então você tem que limitar a um tipo de referência, a fim de obter a conversão implícita de T [] para que [].
Tente isto:
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();
}