4

我正在尝试用 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 编译。

4

8 回答 8

7

为 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) };
    }
}
于 2009-06-17T12:44:09.953 回答
5

这里的问题是 C# 不支持泛型中的变(至少直到 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();
于 2009-06-17T12:51:51.827 回答
3

我假设你已经尝试过

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") };
        }
    }
}

还是我只是搞砸了一个要求?

于 2009-06-17T12:45:24.790 回答
1

尝试添加通用约束

其中 T:I

于 2009-06-17T12:41:17.137 回答
1

我猜这里的问题是泛型没有意识到 T 实现了 I。明确声明 T : I 可能有效。

您也可以执行 for 循环并一次添加一个 T 对象,而不是使用 AddRange。

于 2009-06-17T12:43:29.457 回答
1

c# 的类型结构目前不支持这一点(将Foo<T>视为Foo<I>if X : I),这被称为 I 上的协方差。

底层框架确实如此,并且c# 4.0 正在添加对它的支持

由于需要这样的显式转换,马克的答案是最简单的。

于 2009-06-17T12:57:06.403 回答
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();
于 2009-06-17T12:59:21.717 回答
0

引用对象数组在 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();
}
于 2009-06-17T14:55:06.617 回答