可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am trying to accomplish something in C# that I do easily in Java. But having some trouble.
I have an undefined number of arrays of objects of type T.
A implements an interface I.
I need an array of I at the end that is the sum of all values from all the arrays.
Assume no arrays will contain the same values.
This Java code works.
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()]);
However this C# code doesn't:
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();
So it's obvious I need to somehow get the array of T[]
into an IEnumerable<I>
to add to the list but I'm not sure the best way to do this? Any suggestions?
EDIT: Developing in VS 2008 but needs to compile for .NET 2.0.
回答1:
The issue here is that C# doesn't support co-variance (at least not until C# 4.0, I think) in generics so implicit conversions of generic types won't work.
You could try this:
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
{
list.AddRange(Array.ConvertAll<T, I>(arrayOfA, t => (I)t));
}
return list.ToArray();
For anyone that strumbles across this question and is using .NET 3.5, this is a slightly more compact way of doing the same thing, using Linq.
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
{
list.AddRange(arrayOfA.Cast<I>());
}
return list.ToArray();
回答2:
Edited for 2.0; it can become:
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();
}
How about (in .NET 3.5):
I[] arr = src.SelectMany(x => x).Cast<I>().ToArray();
To show this in context:
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) };
}
}
回答3:
I assume you have tried
list.AddRange((I[])arrayOfA);
already?
EDIT In reply to your comment saying that my suggestion wouldn't work: I successfully ran this code just a minute ago:
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") };
}
}
}
Or did I just muss a requirement?
回答4:
Try adding a generic constraint
where T:I
回答5:
I'm guessing the problem here is that generics doesn't realize that T implements I. It might work to declare explicitly T : I.
Also you could do a for loop and add your T objects one at a time instead of using AddRange.
回答6:
The type structure of c# does not currently support this (treating the Foo<T>
as a Foo<I>
if X : I) this is termed covariance on I.
The underlying framework does, and c# 4.0 is adding support for it
As such explicit casts are required, Marc's answer being the simplest.
回答7:
In your C# code you are not adding the arrayOfA to the result list:
List<I> list = new List<I>();
foreach (T[] arrayOfA in arrays)
list.AddRange(arrayOfA);
return list.ToArray();
However, if you are using .NET 3.5 you can do this with LINQ:
return (from arrayOfA in arrays
from element in arrayOfA
select element as I).ToArray();
Or using LINQ methods:
return arrays.SelectMany(arrayOfA => arrayOfA.Cast<I>()).ToArray();
回答8:
Arrays of reference objects are covariant in C# (the same is true for Java).
From the name, I guess that your T is a generic and not a real type, so you have to restrict it to a reference type in order to get the implicit conversion from T[] to I[].
Try this:
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();
}