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