Justin
Justin

Reputation: 86729

Constructing generic lists of anonymous types and nested loops

I wish to construct a list of anonymous types constructed by iterating through two other lists in a nested loop.

var myList = new List<???>();
foreach (object x in GetAllX())
{
    if (Process(x))
    {
        foreach (object y in GetAllY(x))
        {
            myList.Add(new {
                X = x,
                Y = y
            });
        }
    }
}

I know that I can construct a List of anonymous types using ToList(), (see this question), but I can't see how I can use this in the above case.

Note that I can't change the GetAllX and GetAllY methods.

Upvotes: 2

Views: 2371

Answers (4)

Enigmativity
Enigmativity

Reputation: 117064

Wouldn't this code be the simplest way to achieve your desired result?

var myList = (from x in GetAllX()
              where Process(x)
              from y in GetAllY(x)
              select new
              {
                  X = x,
                  Y = y,
              }).ToList();

(Timwi - I know this is the "linqified" version of your solution, but I thought I would post it as I feel that in this style it is easy to read and follow.)

Upvotes: 1

Cheng Chen
Cheng Chen

Reputation: 43523

Since you put X and Y in the same list, they must have a common base class/interface.What? No relations between them? Then why do you put them in the same list! That's not a good idea.

IEnumerable<BaseClass> AllXY = 
            GetAllX().Cast<BaseClass>().Union(GetAllY().Cast<BaseClass>());
foreach(var base in AllXY)
{
    //do something to base, usually with polymorphism
}

Upvotes: 0

Timwi
Timwi

Reputation: 66573

The simple answer is “you shouldn’t”.

There is a hacky trick which allows you to do it:

var myList = new[] { new { X = (object) null, Y = (object) null } }.ToList();
myList.Clear();
foreach (object x in GetAllX())
    // ...

But it would really be more reasonable to use it the way it was intended:

var myList = GetAllX().Where(x => Process(x))
    .SelectMany(x => GetAllY(x).Select(y => new { X = x, Y = y }))
    .ToList();

If you really can’t use this pure-functional style for some reason, or you find you have to instantiate such a list in multiple places, you should probably declare a normal class instead of using an anonymous type. Remember that anonymous types are compiled into classes anyway, so there is no performance benefit to anonymous types, and even the readability/maintainability benefit is questionable if you have to resort to tricks like the hacky one at the top of this post.

Some people suggest to use List<dynamic>, but I recommend against it. It severely hampers maintainability because the property names and types are no longer checked at compile-time (you could mistype one and get a run-time bug); it slows down run-time performance because every access goes through the dynamic dispatcher; and also, once you put your objects into this list, you are basically stuck with them being dynamic, because you can’t cast them back to the anonymous type.

Upvotes: 5

Jeff Mattfield
Jeff Mattfield

Reputation:

You have two choices here. First, you could create a simple class that has X and Y properties and make your list a list of objects of that class, like so:

class NewClass
{
    public object X;
    public object Y;
}

var myList = new List<NewClass>();
foreach (object x in GetAllX())
{
    if (Process(x))
    {
        foreach (object y in GetAllY(x))
        {
            myList.Add(new NewClass() {
                X = x,
                Y = y
            });
        }
    }
}

Or, you could just use this in C# 4.0:

var myList = new List<dynamic>();

Upvotes: 0

Related Questions