A lesson in LINQ deferred execution
I learned something interesting about LINQ today, and I’m surprised that it took me this long to run into this.
Do you think this test would pass?
public class Account
{
public int AccountId { get; set; }
public string Status { get; set; }
}
[TestClass]
public class Test
{
[TestMethod]
public void Deferred_execution_test()
{
var accountIds = new[] {1, 2, 3};
IEnumerable list = accountIds.Select(accountId => new Account {AccountId = accountId});
foreach (var account in list)
account.Status = "Active";
foreach (var account in list)
Assert.IsTrue(account.Status == "Active");
}
}
It actually fails.
I'm sure when you've been debugging code you've at some point seem something like this:
The key is the "Expanding the Results View will enumerate the IEnumerable". This means that it hasn't yet executed the code in the Select()
clause. So every time you access the IEnumerable<T>, it's going to execute the Select()
statement, which means that it will new up new Account objects every time.
This test will pass:
public class Account
{
public int AccountId { get; set; }
public string Status { get; set; }
}
[TestClass]
public class Test
{
[TestMethod]
public void Deferred_execution_test()
{
var accountIds = new[] {1, 2, 3};
IEnumerable list = accountIds.Select(accountId => new Account {AccountId = accountId}).ToList();
foreach (var account in list)
account.Status = "Active";
foreach (var account in list)
Assert.IsTrue(account.Status == "Active");
}
}
All I did was add .ToList()
to the end of the statement after the Select()
. This executes the Select()
statements and stores and actual List<T>
in the "list" variable instead of a WhereSelectArrayIterator
(which has deferred execution).
Just something to keep in mind if you're using Select()
to new up some objects.
yes ienumerables are read only.