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开发者_Go百科 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.
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.
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.)
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>();
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
}
精彩评论