Преобразование массива типа T в массив типа I, где T реализует I в C#
-
06-07-2019 - |
Вопрос
Я пытаюсь выполнить что-то на C #, что я легко делаю на Java.Но возникли некоторые проблемы.У меня есть неопределенное количество массивов объектов типа T.A реализует интерфейс I.Мне нужен массив I в конце, который является суммой всех значений из всех массивов.Предположим, что ни один массив не будет содержать одинаковых значений.
Этот 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()]);
Однако этот код C # не:
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();
Итак, очевидно, что мне нужно каким-то образом получить массив T[]
в IEnumerable<I>
чтобы добавить в список, но я не уверен, что это лучший способ сделать это?Есть какие-нибудь предложения?
Редактировать:Разрабатывается в VS 2008, но нуждается в компиляции для .NET 2.0.
Решение
Проблема в том, что C # не поддерживает co-variance (по крайней мере, до C # 4.0, я думаю) в дженериках, так что неявные преобразования дженериков не будут работать.
Вы можете попробовать это:
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
{
list.AddRange(Array.ConvertAll<T, I>(arrayOfA, t => (I)t));
}
return list.ToArray();
<Ч>
Для любого, кто сталкивается с этим вопросом и использует .NET 3.5, это немного более компактный способ сделать то же самое, используя Linq.
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
{
list.AddRange(arrayOfA.Cast<I>());
}
return list.ToArray();
Другие советы
Отредактировано для 2.0; это может стать:
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();
}
Как насчет (в .NET 3.5):
I[] arr = src.SelectMany(x => x).Cast<I>().ToArray();
Чтобы показать это в контексте:
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) };
}
}
Я предполагаю, что вы уже пробовали
list.AddRange((I[])arrayOfA);
уже?
Редактировать В ответ на ваш комментарий о том, что мое предложение не сработает:Я успешно запустил этот код всего минуту назад:
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") };
}
}
}
Или я просто перепутал требование?
Попробуйте добавить общее ограничение
где T: I
Я предполагаю, что проблема здесь в том, что дженерики не понимают, что T реализует I. Это может сработать, чтобы объявить явно T: I.
Также вы можете сделать цикл for и добавлять свои объекты T по одному вместо использования AddRange.
Структура типов c # в настоящее время не поддерживает это (обрабатывая Foo<T>
как Foo<I>
, если X: I), это называется ковариацией для I.
Базовая структура делает, и c # 4.0 является добавив поддержку для этого
Поскольку такие явные приведения необходимы, ответ Марка самый простой.
В вашем коде C # вы не добавляете arrayOfA в список результатов:
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
list.AddRange(arrayOfA);
return list.ToArray();
Однако, если вы используете .NET 3.5, вы можете сделать это с помощью LINQ:
return (from arrayOfA in arrays
from element in arrayOfA
select element as I).ToArray();
Или используя методы LINQ:
return arrays.SelectMany(arrayOfA => arrayOfA.Cast<I>()).ToArray();
Массивы ссылочных объектов ковариантны в C # (то же самое верно и для Java).
Судя по названию, ваш T является универсальным, а не реальным типом, поэтому вы должны ограничить его ссылочным типом, чтобы получить неявное преобразование из T [] в I [].
Попробуйте это:
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();
}