views:

424

answers:

1

I'm perhaps being a bit lazy asking this here, but I'm just getting started with LINQ and I have a function that I am sure can be turned into two LINQ queries (or one nested query) rather than a LINQ and a couple of foreach statements. Any LINQ gurus care to refactor this one for me as an example?

The function itself loops through a list of .csproj files and pulls out the paths of all the .cs files included in the project:

    static IEnumerable<string> FindFiles(IEnumerable<string> projectPaths)
    {            
        string xmlNamespace = "{http://schemas.microsoft.com/developer/msbuild/2003}";
        foreach (string projectPath in projectPaths)
        {
            XDocument projectXml = XDocument.Load(projectPath);
            string projectDir = Path.GetDirectoryName(projectPath);

            var csharpFiles = from c in projectXml.Descendants(xmlNamespace + "Compile")
                              where c.Attribute("Include").Value.EndsWith(".cs")
                              select Path.Combine(projectDir, c.Attribute("Include").Value);
            foreach (string s in csharpFiles)
            {
                yield return s;
            }
        }
    }
+8  A: 

How about:

        const string xmlNamespace = "{http://schemas.microsoft.com/developer/msbuild/2003}";

        return  from projectPath in projectPaths
                let xml = XDocument.Load(projectPath)
                let dir = Path.GetDirectoryName(projectPath)
                from c in xml.Descendants(xmlNamespace + "Compile")
                where c.Attribute("Include").Value.EndsWith(".cs")
                select Path.Combine(dir, c.Attribute("Include").Value);
Marc Gravell
brilliant. I knew StackOverflow would find me the answer quicker than I could find it myself by reading a LINQ book! thanks a lot.
Mark Heath
No problem; as a minor optimisation you might "let inc = c.Attribute("Include").Value", and then where inc.EndsWith(..) select inc...
Marc Gravell