【问题标题】:How many times it is ordered a IENumerable?它被订购了多少次 IENumerable?
【发布时间】:2017-07-17 10:25:01
【问题描述】:

我知道当我迭代一个 IEnumerable 时,我会迭代源集合,所以如果我在 IEnumerable 的下一次迭代中修改了源集合,它会计算修改次数。

所以我想知道当我有一个有序的 IEnumerable 时它会如何影响它。

例如,如果我有这个:

List<MyType> myNoOrderedList //add my items

IEnumerable<MyType> myOrderedIEnumerable = myNoOrderedList
  .OrderBy(x => x.Property);

foreach(MyType in myOrderedIEnumerable)
{
    //Do something
}

假设我在列表中有 3 个元素,在 IEnumerable 上的每次迭代中,列表是有序的还是只有列表被排序一次?

如果我在“做某事”中添加或删除项目会怎样? IEnumerable 有初始订购项目还是必须再次订购才能考虑到列表的修改?

【问题讨论】:

  • AFAIK LINQ 生成的枚举每次都会被重新枚举,所以如果你修改基本列表并迭代顺序仍然是正确的。但是,自己检查一下是微不足道的。
  • 只需运行代码并自行检查 - 会比在这里询问要快得多;)
  • 如果我在“做某事”中添加或删除一个项目会发生什么? - 运行它时你会得到非常快的答案 - 格式为异常

标签: c# ienumerable


【解决方案1】:

答案:

  1. 最多一次(在具体化,如果有的话)
  2. 由于您已具体化myOrderedIEnumerable,因此不会看到对初始myNoOrderedList 的任何修改:

简化示例:

 List<string> initial = new List<String>() {
   "a", "z", "e", "d";
 };

 // Nothing will be done at this point (no ordering)
 var ordered = initial.
   .OrderBy(x => x.Property);

 // ordered has not materialized here, so it'll feel Addition
 initial.Add("y"); // <- will be added into ordered 

 // Here on the first loop ordered will be materialized and since
 // initial and ordered are different collection now, we can modify 
 // initial without changing ordered
 foreach (var item in ordered) {
   if (item == "a") {
     initial.Add("b");
     initial.Remove("z");
   } 

   Console.WriteLine(item);
 }

结果:

a
d
e
y <- before materialization
z 

编辑:请注意,物化是一件棘手的事情:它可能被称为:

  1. 立即,在声明处,例如在.ToList().Any().FirstOrDefault() 之后
  2. 在第一个项目上,例如.OrderBy(你的情况)
  3. 从不.Where(), .SkipWhile() - 查看 Magnus 的评论

Linq 是惰性的,并且尽可能晚地执行物化。

【讨论】:

  • 但是如果查询看起来不同,只包含一个 where 例如它会影响 foreach 循环。因此,根据查询,“物化”的行为会有所不同。
  • 您好 Dmitry,您能否将此答案扩展到多线程场景或至少是评论?非常感谢!
  • @Sharky: 坏消息: List&lt;T&gt; (initial) 不是线程安全的,这就是为什么多线程场景 如果您尝试修改列表,则不适合它(您将获得不稳定的结果)
  • @Magnus:你说的很对,谢谢! Linq 是懒惰的,这就是 nmaterialization 很棘手的原因;这个事实值得一提。
  • @DmitryBychenko 感谢 Dmitry 的回复!
【解决方案2】:

假设我在列表中有 3 个元素,在 IEnumerable 上的每次迭代中,列表是有序的还是只有列表被排序一次?

根据您当前的代码,只有一次。
每次获得枚举数时,都会对列表进行排序。
你的foreach 语句将被编译成try..finally,只有一个枚举器用于遍历集合。

如果我在“做某事”中添加或删除一个项目会怎样? IEnumerable 有初始订购项目还是必须再次订购才能考虑到列表的修改?

如果在从 IOrderedEnumerable 获取枚举数之前从 myNoOrderedList 添加或删除项目,则它将包含在排序结果中。
如果您在开始枚举已排序的集合之后执行此操作,那么它不会以任何方式影响您的活动枚举,因为已排序的项目被缓冲并从缓冲区返回。

但是,请记住,如果您可以在枚举集合中添加或删除项目(在您的情况下为 miOrderedIEnumerable),那么您会收到 InvalidOperationException 说“集合已修改;枚举操作可能无法执行”。

【讨论】:

  • 对不起,第二个答案不正确。您不能在foreach 中更改miOrderedIEnumerable,但您可以在myNoOrderedList 上调用AddRemoveAtClear...。但是,当myNoOrderedList被修改时,miOrderedIEnumerable不会改变。
  • @DmitryBychenko 当然,你是对的,我的错:(我已经更新了我的答案。
【解决方案3】:

每次需要OrderBy 的结果时,都需要一个buffered copy of the source is sorted。例如:

int[] array = { 2, 1, 3 };
var ordered = array.OrderBy(x => x);

foreach (int i in ordered)
{
    array[1] = 0;
    Debug.Write(i);             // 123
}

foreach (int i in ordered)
    Debug.Write(i);             // 023

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    • 2012-05-01
    • 2020-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多