LINQ延迟执行时属性更新失效及无空引用异常的原因问询
问题解答
代码重现
#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
控制台输出
,
问题1:foreach中设置的FamilyName为何未生效?
LINQ的Select操作是延迟执行的,它不会立即生成结果集合,而是在每次遍历(比如foreach、ToList())时才会执行传入的委托逻辑(也就是new Person{ Name = x })。
这里的核心问题是:
- 第一次遍历
persons(foreach循环)时,会为每个userNames元素创建一个Person实例,并设置其FamilyName,但这些实例在遍历结束后就被丢弃了,没有被保存下来。 - 第二次调用
persons.Select(...).ToList()时,会重新遍历userNames,再次创建全新的Person实例,这些新实例的FamilyName从未被设置过,自然都是null,所以最终输出两个null拼接的结果。
解决办法:在第一次遍历前,把persons转换成立即执行的集合(比如调用ToList()),这样后续操作都基于同一个实例集合:
var persons = userNames.Select(x => new Person{ Name = x }).ToList();
问题2:为何设置FamilyName时未抛出NullReferenceException?
延迟执行不等于“不创建实例”——当你执行foreach (var person in persons)时,已经触发了Select委托的执行,每个循环迭代都会创建一个Person实例,所以person变量是有效的非空引用,设置它的FamilyName属性自然不会抛出空引用异常。
只是这些实例在foreach循环结束后没有被保留,后续第二次遍历persons时又生成了新的实例而已。
内容的提问来源于stack exchange,提问作者Pelle Stenild Coltau
相关产品推荐
相关产品推荐

