TがC#でIを実装する、T型の配列をI型の配列に変換する
-
06-07-2019 - |
質問
Javaで簡単にできることをC#で実現しようとしています。しかし、いくつかの問題を抱えています。 タイプTのオブジェクトの未定義の配列があります。 インターフェース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#が共分散(少なくとも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);
すでに?
編集私の提案が機能しないというコメントへの返信:ほんの1分前にこのコードを正常に実行しました:
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を明示的に宣言することで動作する可能性があります。
また、AddRangeを使用する代わりにforループを実行し、Tオブジェクトを1つずつ追加することもできます。
c#の型構造は現在これをサポートしていません(XがIの場合Foo<T>
をFoo<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();
}