Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What magic is this?

Tags:

c#

xml

linq

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?

like image 691
Kittoes0124 Avatar asked Dec 06 '22 09:12

Kittoes0124


2 Answers

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.

like image 56
Servy Avatar answered Dec 22 '22 14:12

Servy


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.

like image 27
Marc Gravell Avatar answered Dec 22 '22 16:12

Marc Gravell