为什么使用“yield”延迟执行会出现不同的运行时行为? C# 中的关键字?
如果您在下面的示例代码中调用 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;
}
If you call the IgnoreNullItems
extension method in the sammple code below the deferred execution works as expected however when using the IgnoreNullItemsHavingDifferentBehaviour
the exception is raised immediately. Why?
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?
Thanks for sharing your ideas!
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;
}
}
Here a version with the same behaviour:
Here a version that shows the same behaviour. Don't let resharper "improve" your foreach statement in this case ;) --> resharper changes the foreach to the "IgnoreNullItemsHavingDifferentBehaviour" version with a return statement.
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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
由于 IgnoreNullItemsHavingDifferentBehaviour 本身不包含任何“yield”,因此会立即引发异常。
相反,它是IgnoreNulls,它被转换为迭代器块,从而使用延迟执行。
这实际上是 Jon Skeet 在他的 EduLinq 系列中使用的方法,强制立即对源序列进行空检查。请参阅
The exception is raised immediately because IgnoreNullItemsHavingDifferentBehaviour doesn't contain any "yield" itself.
Rather, it's IgnoreNulls which gets converted into an iterator block and thus uses deferred execution.
This is actually the approach that Jon Skeet used in his EduLinq series to force immediate null checks for source sequences. See this post for a more detailed explanation (specifically the "Let's implement it" section).
我还没有测试过,但我可以猜测...
使用
IgnoreNullItems
方法,整个方法被推迟,直到你成为枚举。使用您的替代方法,仅延迟IgnoreNulls
的执行 -IgnoreNullItemsHavingDifferentBehaviour
中的 null 检查立即发生。I haven't tested, but I can guess...
With the
IgnoreNullItems
method, the whole method is deferred until you being the enumeration. With your alternate method, only the execution ofIgnoreNulls
is deferred - the null check inIgnoreNullItemsHavingDifferentBehaviour
happens immediately.延迟执行来自于yield return 的工作原理。
它将在方法内创建状态机,在您尝试枚举第一项之前,该状态机不会启动或执行任何代码。
但是当没有
yield return
时,它的行为就像普通方法一样。Jon Skeet 的 Edulinq.
Deferred execution comes from how
yield return
works.It will create the state machine inside a method, that will not start or do any code until you try to enumerate first item.
But when there is no
yield return
it will behave like normal method.Perfectly explained and shown in Jon Skeet's Edulinq.