【问题标题】:Foreach, extension methods, and IEnumerable [closed]Foreach、扩展方法和 IEnumerable [关闭]
【发布时间】:2018-09-06 03:46:15
【问题描述】:

出于好奇,我只是一个简短的问题。因此,根据 MSDN 关于 foreach 循环的文档,它实现了 System.Collections.IEnumerable。文档中还有一个警告,您不能在使用 foreach 循环时修改集合(我假设 foreach 循环在这方面不是非常线程安全的?)

文档https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/foreach-in

另外,出于好奇,在速度方面,foreach 循环是否比 for 循环更优化?我知道 foreach 循环更容易输入/阅读,但它的执行速度是否比 for 循环快?

查看 MSDN 文档中的列表,扩展方法 Average 是由 Enumerable 定义的。当文档说它对每个元素执行转换功能时,文档是什么意思? 这是否意味着扩展方法的行为类似于 foreach 循环? 文档https://msdn.microsoft.com/en-us/library/bb549067(v=vs.110).aspx

假设地说,假设我有两个线程。假设我有一个由这两个线程共享的列表。第一个线程正在遍历这个列表(foreach),第二个线程正在使用这个列表上的 Average 扩展方法。那么这会导致抛出异常吗?我知道使用锁可以避免任何潜在的问题,但为了讨论,假设我没有使用锁。

感谢您的宝贵时间。

【问题讨论】:

  • 循环遍历列表和Average() 都只从列表中读取,所以这不会导致异常。 foreach 的速度取决于GetEnumerator() 的实现方式。
  • 您是否尝试过运行一些基准测试?您在处理集合时无法更改集合,这与线程安全无关,但是,例如,如果您执行 for 并删除最后一项,则会出现堆栈溢出。
  • 也许它只是拼写错误,但您似乎对 foreach 循环的作用有误解。循环本身不实现IEnumerable,它只允许迭代实现IEnumerable对象。关于在循环时修改集合的部分不是线程安全问题,因为循环正在迭代集合,如果集合正在改变,你怎么能合理地迭代它?
  • 如果您仔细阅读how to askon-topic 中的内容,您会发现SO 并不是真正适合“假设”和“假设”的地方。向我们展示您的代码有问题 - 或自己测试。
  • 只是一个更正,我不记得确切的例外,但您可以自己查看。 ;)

标签: c# foreach extension-methods enumerable


【解决方案1】:

所以根据 MSDN 关于 foreach 循环的文档,它实现了 System.Collections.IEnumerable

没有。任何支持GetEnumerator() 方法的东西又返回一个支持bool MoveNext() 方法和Current 属性的对象将支持foreachIEnumerableIEnumerable<T> 都支持这一点,因此实现它们的所有东西都将允许 foreach,但这不是唯一的方法。这不仅是一种方便的方式,而且还可以与采用 IEnumerable<T> 的方法(包括 linq 扩展方法)一起使用,因此当您想要支持 foreach 时,通常最好采用这种方法。 (您可能会选择支持像 List<T> 这样的其他方式,但这很少值得。

另外,出于好奇,在速度方面,foreach 循环是否比 for 循环更优化?

这很复杂,因为foreach 是什么,for 是什么。 for 具有优势是很常见的,因为它涉及的虚拟调用较少,但是当 foreach 更快时也有例外。如果您直接对数组进行操作,则更复杂的是,foreach 会更慢,除非编译器实际上捕获了这一点并在幕后为您将其重写为for,因此它们完全相同。

当文档说它对每个元素执行转换功能时,它是什么意思。

这意味着Average 的特定重载将每个元素传递给对传递给它的selector 的调用。这是这个变换的结果被平均了。

这是否意味着扩展方法的行为类似于 foreach 循环?

是的。事实上,他们经常是foreach。该特定方法是 .NET Framework 版本中的 foreach 和 .NET Core 版本中对 foreach 的轻微优化。许多方法在两个版本中都是foreach。某些方法在已知性能更高的少数情况下使用for(请参阅上文,了解有时是这种情况,有时不是)。在绝大多数情况下,所做的大部分优化都不值得,但 Linq 方法被很多人大量使用,所以即使是一点点优化的努力也会得到回报。

第一个线程正在遍历这个列表 (foreach),第二个线程正在使用这个列表上的 Average 扩展方法。那么这会导致抛出异常吗?

在这种特殊情况下,只要没有线程写入列表,两者就不会相互造成任何问题。用于实现foreach 的对象是在每个线程中单独创建的,它们是唯一发生变异的对象。 List<T> 的文档不保证这一点,但不太可能改变。

这并不适用于所有支持foreach 的东西。多线程 foreach 没有任何承诺,除非是给定的实现。

【讨论】:

    猜你喜欢
    • 2019-03-17
    • 2012-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-03
    • 2016-02-01
    相关资源
    最近更新 更多