Convert an array of type T to an array of type I, where T implements I in C #

I am trying to do something in C #, which I easily do in Java. But there are problems. I have an undefined number of arrays of objects of type T. A implements the interface I. I need the array I at the end, which is the sum of all the values ​​from all arrays. Assume that 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 does not:

 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, obviously, I need to somehow get an array of T[] in IEnumerable<I> to add to the list, but I'm not sure if this is the best way to do this? Any suggestions?

EDIT: development in VS 2008, but it needs to be compiled for .NET 2.0.

+4
source share
8 answers

The problem is that C # does not support co-variance (at least until C # 4.0, I think) in generics, implicit conversions of generic types will not work.

You can try the following:

 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 those who have tried this question and are using .NET 3.5, this is a slightly more compact way to do the same using Linq.

 List<I> list = new List<I>(); foreach (T[] arrayOfA in arrays) { list.AddRange(arrayOfA.Cast<I>()); } return list.ToArray(); 
+5
source

Edited for version 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) }; } } 
+7
source

I assume you tried

 list.AddRange((I[])arrayOfA); 

already?

EDIT In response to your comment, which says that my suggestion will not work: I successfully executed 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 was I just looking for a call?

+3
source

Try adding a general restriction.

where T: I

+1
source

I assume the problem is that generics do not understand that T implements I. T: I can be explicitly declared.

You can also do a for loop and add your T objects one at a time, rather than using AddRange.

+1
source

A C # type structure does not currently support this (handling Foo<T> as Foo<I> if X: I), this is called covariance on I.

The basic structure, and C # 4.0 adds support for it

Since such explicit drops are required, Marc's answer is the simplest.

+1
source

In C # code, you are not adding arrayOfA to the list of results:

 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(); 
0
source

Arrays of reference objects are covariant in C # (the same is true for Java).

From the name, I assume that your T is a generic, not a real type, so you need to restrict it to a reference type in order to get an implicit conversion from T [] to i [].

Try the following:

 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(); } 
0
source

Source: https://habr.com/ru/post/1286344/


All Articles