Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

LINQ deferred execution when updating a property

I have the following C# code:

#nullable enable

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        var userNames = new List<string>{"Jon Doe", "Jane Doe"};
        var persons = userNames.Select(x => new Person{ Name = x });
        
        foreach (var person in persons)
        {
            person.FamilyName = person.Name.Split(' ').Last();
        }

        var familyNameList = persons.Select(x => x.FamilyName).ToList();
        Console.WriteLine(string.Join(", ", familyNameList));
    }

    private class Person
    {
        public required string Name { get; init; }

        public string? FamilyName { get; set; }
    }
}

#nullable disable

The console outputs:

 , 

And upon inspecting the familyNameList list, I find that it only contains null values.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

I understand that deferred execution is in play, but why is the FamilyName not set in the foreach loop?

And if the Person is not instantiated before .ToList() is called, then why is there no NullReferenceException thrown when trying to set the FamilyName property?

Link to .NET Fiddle: https://dotnetfiddle.net/VMKXEw

>Solution :

In short – materialize the enumerable:

var persons = userNames.Select(x => new Person{ Name = x }).ToList();

I understand that deferred execution is in play, but why is the FamilyName not set in the foreach loop?

It is set but the results are effectively discarded. I.e. the following:

var familyNameList = persons.Select(x => x.FamilyName).ToList();
Console.WriteLine(string.Join(", ", familyNameList));

Will iterate a completely new collection of freshly created persons. LINQ is lazy and the actual execution is deferred until one of the materializable operations (Count, foreach, ToList, First, etc.). And the whole enumerable will be enumerated for every such operation (with the exception of some optimizations like for Count when the passed IEnumerable<T> is actually materialized and has count).

Just add a side-effect to the original enumerable:

var persons = userNames.Select(x =>
{
    Console.WriteLine(x);
    return new Person{Name = x};
});

And the output will speak for itself.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading