【问题标题】:Why are IEnumerable Projections not Immutable?为什么 IEnumerable 投影不是不可变的?
【发布时间】:2015-06-02 23:40:44
【问题描述】:

假设我的Awesome 类有一个属性Bazinga,这是一个默认值为“默认值”的String,我可以去:

    var enumerableOfAwesome = Enumerable
        .Range(1, 5)
        .Select(n => new Awesome());

    foreach (var a in enumerableOfAwesome)
        a.Bazinga = "New value";

您可能已经知道,如果您希望在所有 Bazinga 中安全地找到您的“新值”字符串,那么再次枚举 enumerableOfAwesome 不会让您感到高兴。相反,将它们输出到控制台会呈现:

默认值
默认值
默认值
默认值
默认值

(.NET Fiddle here)

从某些延迟执行的角度来看,这一切都很好,而且这个问题之前已经讨论过,比如herehere。然而,我的问题是为什么枚举器实现不返回不可变对象;如果不能保证多次枚举的持久性,那么设置值的能力有什么用,除了让人困惑吗?

也许这不是一个很好的问题,但我相信你的回答会很有启发性。

【问题讨论】:

  • ToList() 或 ToArray() 在这种情况下是你的朋友。了解他们的工作可能会帮助您了解正在发生的事情。
  • 因为 C# 中没有神奇的不变性包装器,所以枚举只是返回一个定义其自身状态和可变性的类型的实例。
  • LINQ 查询实际上只是一组指令,它们定义了应该在某个集合上执行什么以及返回一个新集合。如果您执行两次,您将对集合执行两次指令——就是这样。如果您不将这些结果存储在集合中并使用该集合,您将从同一点开始两次。

标签: c# linq


【解决方案1】:

然而我的问题是为什么枚举器实现不返回不可变对象

您希望他们如何做到这一点?它们是使用任何类型的序列 - 您希望发生什么,例如:

var builders = new string[] { "foo", "bar" }.Select(x => new StringBuilder(x));

?您是否期望它(在这种情况下,无论“它”是什么)自动创建一个具有与StringBuilder 相同的 API 的新类型,但神奇地使其不可变?它如何检测突变?

基本上,您要求的内容是不可行的 - 您可以拥有一个元素类型可变的序列,因此在迭代它时,您可以改变产生的引用所指的对象。期待其他任何事情都是不现实的,IMO。

【讨论】:

  • 我不确定如何应对“不可行”。我意识到这会带来挑战,但是“神奇地使其不可变”对我来说很好。我应该将您的回答读作“这是一个愚蠢的想法”还是“没有实际的解决方案”?
  • @OskarLindberg,这既是一个坏主意,也不切实际。如果你希望你的Awesome 是不可变的,你必须这样写。例如,您可以创建一个名为AwesomeReadOnly 的类,该类可以从作为不可变对象的Awesome 类型进行转换。你如何实现它取决于你,它超出了我认为编译器应该在幕后做的事情来破坏你的代码。在很多情况下,您也希望它是可变的。
  • 匿名类型也已经涵盖了想要轻松投影到不可变事物的解决方案。
  • @PrestonGuillot:不是真的 - 如果你有一个匿名类型,其中一个成员是可变的,它仍然只是 shallow 不变性 - 将我的示例更改为 new { Builder = new StringBuilder(x) } 并且你'd 仍然可以做到item.Builder.Append("xx")
  • @OskarLindberg:虽然 Entity Framework 等“有点神奇”,但 LINQ to Objects 却不是——这就是我喜欢的方式。编译器以非常可预测的方式运行,实现 LINQ to Objects 的实际代码非常简单。我真的不想要创建类型外观的东西,可能在后台克隆对象“以防万一”等。如果你想要不变性,你自己去做吧。
【解决方案2】:

枚举器并不是为了提供不变性。它们是一种遍历集合的方法,就像遍历带有索引的标准数组一样。由于您可以访问底层对象,这意味着您可以更改它们。是的,C# 中的不变性在集合方面更像是一个新事物,这将是出乎意料的,并且不可能在枚举器中实现。

【讨论】:

  • 仅仅因为你可以循环它们并不意味着它们是可变的。你也可以遍历不可变数组/列表/枚举。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-15
  • 2017-02-27
  • 1970-01-01
  • 1970-01-01
  • 2019-06-10
  • 1970-01-01
相关资源
最近更新 更多