【问题标题】:Why does setting a property on an enumerated object not work?为什么在枚举对象上设置属性不起作用?
【发布时间】:2010-09-23 23:19:14
【问题描述】:

我对 C# 了解很多,但这个让我很难过,Google 也帮不上忙。

我有一个 IEnumerable 范围的对象。我想在第一个上设置一个属性。我这样做了,但是当我在修改后枚举对象范围时,我看不到我的变化。

这是一个很好的问题示例:

    public static void GenericCollectionModifier()
    {
        // 1, 2, 3, 4... 10
        var range = Enumerable.Range(1, 10);

        // Convert range into SubItem classes
        var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i});

        Write(items);  // Expect to output 1,2,3,4,5,6,7,8,9,10

        // Make a change
        items.First().MagicNumber = 42;

        Write(items);  // Expect to output 42,2,3,4,5,6,7,8,9,10
        // Actual output: 1,2,3,4,5,6,7,8,9,10
    }

    public static void Write(IEnumerable<SubItem> items)
    {
        Console.WriteLine(string.Join(", ", items.Select(item => item.MagicNumber.ToString()).ToArray()));
    }

    public class SubItem
    {
       public string Name;
       public int MagicNumber;
    }

C# 的哪个方面阻止输出“MagicNumber = 42”更改?有没有一种方法可以让我的更改“坚持”而无需进行一些时髦的转换为 List 或数组?

谢谢! -迈克

【问题讨论】:

    标签: c# ienumerable


    【解决方案1】:

    当您调用 First() 时,它会枚举这段代码的结果:

    Select(i => new SubItem() {Name = "foo", MagicNumber = i});
    

    请注意,Select 是一个惰性枚举器,这意味着它仅在您向其中请求项目时才进行选择(并且每次 都会在您询问时进行)。结果不会存储在任何地方,因此当您调用 items.First() 时,您会得到一个新的 SubItem 实例。然后,当您将项目传递给 Write 时,它​​会获得一大堆新的 SubItem 实例 - 而不是您之前获得的那个。

    如果要存储选择的结果并对其进行修改,则需要执行以下操作:

    var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i}).ToList();
    

    【讨论】:

    • 是的 +1。对于原始问题中定义的items,每次迭代items,都会创建新的SubItem 实例。 .First() 迭代一次,Write 迭代第二次。这样做的另一个后果是(items 仍然与原始问题一样)items.First() == items.First() 评估为 false。
    【解决方案2】:

    我怀疑在后台发生了什么事。很可能是因为 IEnumerables 只能迭代一次。

    在分配给“items”时,如果在调用 Select() 之后添加一个“ToList()”是否有效?

    【讨论】:

    • 是的,如果我将其转换为列表,它会按预期工作。不过我希望避免这种情况。
    • IEnumerables 不能只迭代一次。他们是懒惰的评估,所以每次你迭代你可能会得到不同的结果。但是,添加 ToList() 将解决问题。
    【解决方案3】:

    我唯一能想到的是 items.First() 将 SubItem 的副本传递给您而不是引用,因此当您设置它时,不会进行更改。

    我不得不假设它与 IQueryable 只能迭代一次有关。您可能想尝试更改此设置:

    // Convert range into SubItem classes
    var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i});
    

    到

    // Convert range into SubItem classes
    var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i}).ToList();
    

    然后看看有没有不同的结果。

    【讨论】:

      【解决方案4】:

      您不能/不应该通过枚举器修改集合。我很惊讶这不会引发异常。

      【讨论】:

      • 集合没有被修改,item里面的数据是
      【解决方案5】:

      .First() 是一种方法,而不是属性。它在 Enumerable 的第一个位置返回对象的新实例。

      【讨论】:

      • 它不返回副本,它返回实际对象
      猜你喜欢
      • 2014-02-28
      • 1970-01-01
      • 1970-01-01
      • 2021-11-29
      • 2022-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多