Compartilhar via


Using let in LINQ to Objects

I've been delving into LINQ to Objects recently (and enjoying it), but had missed the 'let' keyword. A colleague Rupert Benbrook(https://phazed.com) and I had been chatting about how to solve a particular issue using LINQ to Objects and he sent me a follow-up email with some code that used the 'let' keyword and I thought I'd take a closer look and see what happens under the covers.

To take a simple example, suppose we have int[] values. We could write the following query to double all the values

var query = from i in values
             select 2 * i;

With the let keyword, this could be rewritten as

 var query = from i in values
        let doublei = 2 * i
        select doublei;

I'll give a slightly more interesting example in a moment, but this simple example lets us easily look at what actually gets generated. After firing up Reflector, it seems that the compiler treats the code above as though we'd typed

 var query = from temp in
        (
            from i in values
            select new { i, doublei = 2 * i }
        )
        select temp.doublei;

So each time you add a let statement into your query, the compiler creates a new subquery that returns an anonymous type composed of the original value plus the new value specified by the let.

For a more interesting example of this, suppose we have IEnumerable<FileSystemInfo> fileSystemInfos, and that we want to pick out the files (i.e. ignore directories) where the size is under 1000 Bytes. We could use the following query

 var query = from fileSystemInfo in fileSystemInfos
            where fileSystemInfo is FileInfo && ((FileInfo)fileSystemInfo).Length < 1000
            select (FileInfo) fileSystemInfo;

This code requires the cast to FileInfo in order to access the Length property, and again to ensure that we return the correct type in the query. Using let, we can perform the cast in a single place

 var query = from fileSystemInfo in fileSystemInfos
        where fileSystemInfo is FileInfo
        let fileInfo = (FileInfo)fileSystemInfo
        where fileInfo.Length < 1000
        select fileInfo;

I think that this version is probably more readable, and if we had more constraints in the where clause that needed the FileInfo rather than FileSystemInfo then the let version would show a bigger difference. Again, the compiler seems to treat the above query as though it was written as

 var query = from temp in
        (
            from fileSystemInfo in fileSystemInfos
            where fileSystemInfo is FileInfo
            select new { fileSystemInfo, fileInfo = (FileInfo)fileSystemInfo }
        )
        where temp.fileInfo.Length < 1000
        select temp.fileInfo;

Having used LINQ to Objects a reasonable amount, I found it interesting to come across a new keyword and was curious to find out how it works - hopefully this post gives an explanation!

Comments

  • Anonymous
    May 29, 2008
    In my previous post , I looked at what the compiler generates when you use the let keyword in LINQ to

  • Anonymous
    May 29, 2008
    Just a quick thought, but your query could be more efficient like this: var query = from fileSystemInfo in fileSystemInfos        let fileInfo = fileSystemInfo as FileInfo        where fileInfo != null && fileInfo.Length < 1000        select fileInfo; The query seems simpler and there's only a single Where clause....

  • Anonymous
    July 12, 2008
    Yes, nice catch - using 'as' works really nicely here

  • Anonymous
    July 14, 2008
    This is a follow-up to my two previous posts on the let keyword Using let in LINQ to Objects Using let

  • Anonymous
    March 27, 2011
    And why not simply    from fileInfo in fileSystemInfos.OfType<FileInfo>           where fileInfo.Length < 1000           select fileInfo; or, as I prefer:    fileSystemInfos.OfType<FileInfo>.Where(fi => fi.Length<1000);

  • Anonymous
    March 27, 2011
    Hi Seth, I was looking for an example to talk about the let keyword, but I agree that the OfType method is a good fit here - thanks for calling it out. For simple queries I also like the conciseness or the extension method approach, and it works well with Single/First as well :-)

  • Anonymous
    November 29, 2012
    Leave a Comment    Name    Comment

  • Anonymous
    June 04, 2013
    Nice article  Stuartle.