为什么使用"yield"延迟执行会有不同的运行时行为?c#中的关键字
本文关键字:quot 运行时 关键字 yield 延迟 执行 为什么 | 更新日期: 2023-09-27 17:53:21
如果您在下面的示例代码中调用IgnoreNullItems
扩展方法,延迟执行会像预期的那样工作,但是当使用IgnoreNullItemsHavingDifferentBehaviour
时,异常会立即引发。为什么?
List<string> testList = null;
testList.IgnoreNullItems(); //nothing happens as expected
testList.IgnoreNullItems().FirstOrDefault();
//raises ArgumentNullException as expected
testList.IgnoreNullItemsHavingDifferentBehaviour();
//raises ArgumentNullException immediately. not expected behaviour ->
// why is deferred execution not working here?
感谢分享你的想法!
Raffael Zaghet
public static class EnumerableOfTExtension
{
public static IEnumerable<T> IgnoreNullItems<T>(this IEnumerable<T> source)
where T: class
{
if (source == null) throw new ArgumentNullException("source");
foreach (var item in source)
{
if (item != null)
{
yield return item;
}
}
yield break;
}
public static IEnumerable<T> IgnoreNullItemsHavingDifferentBehaviour<T>(
this IEnumerable<T> source)
where T : class
{
if (source == null) throw new ArgumentNullException("source");
return IgnoreNulls(source);
}
private static IEnumerable<T> IgnoreNulls<T>(IEnumerable<T> source)
where T : class
{
foreach (var item in source)
{
if (item != null)
{
yield return item;
}
}
yield break;
}
}
这里有一个具有相同行为的版本:
这里是显示相同行为的版本。在这种情况下,不要让resharper"改进"你的foreach语句;)——> resharper用return语句将foreach更改为"IgnoreNullItemsHavingDifferentBehaviour"版本。
public static IEnumerable<T> IgnoreNullItemsHavingSameBehaviour<T>(this IEnumerable<T> source) where T : class
{
if (source == null) throw new ArgumentNullException("source");
foreach (var item in IgnoreNulls(source))
{
yield return item;
}
yield break;
}
异常会立即引发,因为IgnoreNullItemsHavingDifferentBehaviour本身不包含任何"yield"。
相反,它是IgnoreNulls,它被转换为迭代器块,因此使用延迟执行。
这实际上是Jon Skeet在他的EduLinq系列中使用的方法,用于强制对源序列进行立即null检查。查看这篇文章获得更详细的解释(特别是"让我们实现它"部分)。
我还没有测试,但我可以猜…
对于IgnoreNullItems
方法,整个方法被延迟到您成为枚举。使用您的替代方法,只有IgnoreNulls
的执行被延迟—IgnoreNullItemsHavingDifferentBehaviour
中的null检查立即发生。
延迟执行来自yield return
的工作方式。它将在一个方法中创建状态机,在您尝试枚举第一项之前,状态机不会启动或执行任何代码。
但是当没有yield return
时,它将像普通方法一样运行。
在Jon Skeet的《Edulinq》中有完美的解释和展示。