tags:

views:

85

answers:

4

A class I want to operate on provides getters of type IEnumerable<X> and IEnumerable<Y> where X & Y are subclasses of base type T. I want to iterate over the contents of both treating them as type T. Is there a handy way to concatenate both into something which can be seen as IEnumerable<T>?

Example:

        IEnumerable<HeaderPart> headers = templateFile.MainDocumentPart.HeaderParts;
        IEnumerable<FooterPart> footers = templateFile.MainDocumentPart.FooterParts;
        List<OpenXmlPart> result = new List<OpenXmlPart>();
        result.Concat<OpenXmlPart>(footers);

HeaderPart and FooterPart are both subclasses of OpenXmlPart but the 3rd line fails:

'System.Collections.Generic.IEnumerable' does not contain a definition for 'Concat' and the best extension method overload 'System.Linq.Enumerable.Concat(System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerable)' has some invalid arguments

Note, I can't change either of the source data, I need to create a new collection - actually I want to do a foreach over it.

+3  A: 

In C#/.NET 4 or newer you can use Enumerable.Concat<T>:

IEnumerable<T> result = xs.Concat<T>(ys);

For your specific case you can use List.AddRange:

List<OpenXmlPart> result = new List<OpenXmlPart>();
result.AddRange(headers.Cast<OpenXmlPart>());
result.AddRange(footers.Cast<OpenXmlPart>());
Mark Byers
None of the types I try have a Concat method... What Enumerable have you got in mind?
John
I got my comments messed up, modified.
John
@John: What version of .NET are you using?
Mark Byers
3.5, I added Linq and still getting all kinds of problems.
John
@Mark: Your code relies on the co-/contra-variance changes introduced in .NET 4. For .NET 3.5 you'd need to do `xs.Cast<T>().Concat(ys.Cast<T>())` instead.
LukeH
@LukeH: +1 Oops... thanks! I was running in new Visual Studio and didn't consider this issue... Oh well, maybe I should leave this anyway since some people do use .NET 4.
Mark Byers
Cool, it seems to be working after casting... I didn't know you could safely cast a collection like that. Now - is there a LINQ query way of doing this, i.e comparable to `SELECT OpenXMLPart part from headers, footers` or is LINQ not quite that advanced?
John
+6  A: 

You can use the Cast function to convert IEnumerable<X> to IEnumerable<T> and then Concat to append the second series

Something like:

listB.Cast<A>().Concat(listC.Cast<A>())
JDunkerley
A: 

If you are sure your IEnumerable is a list you can use AddRange()

IEnumerable<int> list1 = new List<int>() { 1, 2, 3 };
IEnumerable<int> list2 = new List<int>() { 4, 5, 6 };
((List<int>)list1).AddRange(list2);
SchlaWiener
Doesn't work in my code. It says the types aren't compatible since it's not all the same type, but two lists containing different sub-types of the same base. Also the whole point of interfaces is I have no idea the internal type from a .Net class. Two methods return `IEnumerable<X>` and `IEnumerable<Y>` that's all I know.
John
+1  A: 

Haven't tested, and don't know about readymade functionality for it, but instead of allocating new storage and moving data about, you should be able implement your own EnumeratorBinder<T, X, Y> template class, binding the two IEnumerable instances in the constructor, and implement something on the lines of

IEnumerable<T> GetEnumerable()
{
  foreach ( X x in _enumX )
    yield return x;
  foreach ( Y y in _enumY )
    yield return y;
}

with suitable template constraints...

Pontus Gagge
Yep, that's pretty much what LINQ's `Concat` method does. http://msdn.microsoft.com/en-us/library/bb302894.aspx
LukeH
Ah - didn't check the implementation: its the signature suggested list shuffling. Oh, well, it's fun reinventing the wheel!
Pontus Gagge