【问题标题】:Why sometime elements in an IEnumerable<T> are mutable and sometime they are immutable? [duplicate]为什么有时 IEnumerable<T> 中的元素是可变的,而有时它们是不可变的? [复制]
【发布时间】:2020-11-25 14:23:41
【问题描述】:

假设我有一个非常简单的引用类型,称为Person

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

我使用 LINQ 创建一个 IEnumerable 调用 peopleEnum,代码如下。

var ages = new int[] { 1, 2, 3, 4, 5 };

var peopleEnum = ages.Where(a => a > 1)
    .Select(a => new Person
    {
        Name = a.ToString(),
        Age = a
    });

我的理解是因为Person是引用类型,所以peopleEnum里面的元素应该是可变的。但我发现他们不是。

var person = peopleEnum.Single(p => p.Age == 4);
person.Name = "Four";

foreach (var p in peopleEnum)
{
    Console.WriteLine($"{p.Name}, {p.Age}");
}

预期输出。

2, 2
3, 3
Four, 4
5, 5

实际输出。

2, 2
3, 3
4, 4
5, 5

但是,如果我以不同的方式创建 IEnumerable&lt;Person&gt;

var people = new Person[]
{
    new Person{ Name = "1", Age = 1 },
    new Person{ Name = "2", Age = 2 },
    new Person{ Name = "3", Age = 3 },
    new Person{ Name = "4", Age = 4 },
    new Person{ Name = "5", Age = 5 },
};

var peopleEnum2 = people.Where(p => p.Age > 1);

那么peopleEnum2 中的元素将是可变的。

var person = peopleEnum2.Single(p => p.Age == 4);
person.Name = "Four";

foreach (var p in peopleEnum2)
{
    Console.WriteLine($"{p.Name}, {p.Age}");
}

输出

2, 2
3, 3
Four, 4
5, 5

哪些规则确定IEnumerable&lt;T&gt; 中的元素是否可变?

【问题讨论】:

  • 在您的示例中,您枚举了 IEnumerable&lt;T&gt; 两次。当IEnumerable&lt;T&gt; 是一个具体的列表或数组时,多次枚举每次都会产生相同的元素。在您的第一个示例中,每次枚举时都会“动态”和“按需”创建元素,这就是为什么每次都会获得不同的元素。
  • 当您执行Single 时,它会运行创建人员对象的代码。然后,当您运行 foreach 时,它会再次运行该代码,创建 4 个新对象。这称为延迟执行。

标签: c# linq immutability ienumerable


【解决方案1】:

有区别 - 这个IEnumerable 基于ages 数组,每次枚举peopleEnum 时都会执行。这意味着每次将枚举放入 foreach 语句时都会获得新的引用。

var ages = new int[] { 1, 2, 3, 4, 5 };

var peopleEnum = ages.Where(a => a > 1)
    .Select(a => new Person
    {
        Name = a.ToString(),
        Age = a
    });

为了使元素在第一种情况下“可变”,您必须将结果存储到某个静态结构中。例如,使用.ToList()。这有助于确保您只执行一次 Select 语句并保留由它生成的引用

var peopleEnum = ages.Where(a => a > 1)
        .Select(a => new Person
        {
            Name = a.ToString(),
            Age = a
        }).ToList();

【讨论】:

    【解决方案2】:

    这是因为每次迭代peopleEnum枚举,

    var peopleEnum = ages.Where(a => a > 1)
        .Select(a => new Person
        {
            Name = a.ToString(),
            Age = a
        });
    

    创建了一个新的Person。使用数组时,它们被创建一次(并放入数组中)。 因此,在您的情况下,您对其进行迭代并更改人员。当你再次迭代它时,就会创建一个新人。

    【讨论】:

      猜你喜欢
      • 2021-04-14
      • 1970-01-01
      • 1970-01-01
      • 2011-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多