I recently came across a post from the infamous Jon Skeet regarding the use of LINQ to XML. This particular snippet of code caught my eye:
// Customers is a List<Customer>
XElement customersElement = new XElement("customers",
customers.Select(c => new XElement("customer", //This line is "magic"
new XAttribute("name", c.Name),
new XAttribute("lastSeen", c.LastOrder)
new XElement("address",
new XAttribute("town", c.Town),
new XAttribute("firstline", c.Address1),
// etc
));
I decided to test it myself in my application where I had a foreach loop set up like so:
foreach (var kvp in m_jobs) { //m_jobs is a Dictionary<string,Job>
m_xmlDoc.Root.Element("SCHED_TABLE").Add(
kvp.Value.GenerateXmlNode())
);
}
I modifed to:
m_xmlDoc.Root.Element("SCHED_TABLE").Add(
m_jobs.Select(job => job.Value.GenerateXmlNode())
};
Where GenerateXmlNode() is a method that genrates the appropriate XML mark-up for a particular job item. I wasn't sure what would happen but lo and behold it worked exactly as my foreach loop did. What I don't quite understand is WHY?! Also, is this considered an "abuse" or a "feature" of LINQ?
Edit for clarity: I know that .Select will return an IEnumerable containing exactly what I asked for but I'm not expressly enumerating it. I understand how .Add works because it accepts a variable number of arguments but again, I don't expressly enumerate to pass those arguments. So... how does it still work?
The XElement.Add
method will look something like this under the hood:
public void Add(object content)
{
if (content is IEnumerable)
{
foreach (object child in (IEnumerable)content)
Add(child);
}
else
{
//process individual element
}
}
So while it's not clear from the public interface of Add
, you can pass it either a sequence of items, or a single item, and it will determine which it is at runtime and act accordingly.
No magic; the Add
method accepts either a object
or a params object[]
- and internally it simply checks each input for a range of common scenarios, including IEnumerable
etc. It then simply unrolls the sequence, adding the child elements / attributes that it discovers. LINQ returns (in this scenario) an IEnumerable
sequence from Select
, which makes it entire usable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With