Sub*_*Dev 11 c# design-patterns
我正在阅读一本C#书,其中作者(一些名叫Jon Skeet的家伙)实现了Where类似的功能
public static IEnumerable<T> Where<T> ( this IEnumerable<T> source, Funct<T,bool> predicate )
{
if ( source == null || predicate == null )
{
throw new ArgumentNullException();
}
return WhereImpl(source, predicate);
}
public static IEnumerable<T> WhereImpl<T> ( IEnumerable <T> source, Func<T,bool> predicate )
{
foreach ( T item in source )
{
if ( predicate(item) )
{
yield return item;
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我完全理解这是如何工作的,它相当于
public static IEnumerable<T> Where<T> ( this IEnumerable<T> source, Funct<T,bool> predicate )
{
if ( source == null || predicate == null )
{
throw new ArgumentNullException();
}
foreach ( T item in source )
{
if ( predicate(item) )
{
yield return item;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这就提出了一个问题,即为什么会将这些函数分成2个函数,因为会有内存/时间开销,当然还有更多的代码.我总是验证参数,如果我开始写这个例子,那么我将编写两倍的代码.是否有一些思想认为验证和实施应该是单独的功能?
Jon*_*eet 15
原因是迭代器块总是很懒惰.除非你调用GetEnumerator()然后MoveNext(),方法中的代码将不会被执行.
换句话说,考虑这种对"等效"方法的调用:
var ignored = OtherEnumerable.Where<string>(null, null);
Run Code Online (Sandbox Code Playgroud)
没有异常被抛出,因为你没有打电话GetEnumerator()然后MoveNext().将其与我的版本进行比较,无论返回值如何使用,都会立即抛出异常...因为它只是在热切地验证之后才使用迭代器块调用该方法.
请注意,async/await具有类似的问题 - 如果您有:
public async Task FooAsync(string x)
{
if (x == null)
{
throw new ArgumentNullException(nameof(x));
}
// Do some stuff including awaiting
}
Run Code Online (Sandbox Code Playgroud)
如果你打电话给这个,你最终会得到一个错误Task- 而不是NullReferenceException被抛出.如果等待返回Task,则抛出异常,但这可能不是您调用方法的地方.在大多数情况下这没关系,但值得了解.
它可能取决于场景和您的编码风格.当您使用yield创建迭代器时,Jon Skeet绝对正确地说明为什么它们应该分开.
顺便说一句,我认为在这里添加我的两分钱可能很有意思:使用代码合同(即按合同设计)的相同代码以不同的方式运行.
前置条件不是迭代器块的一部分,因此,如果不满足整个前置条件,则以下代码将立即抛出合同异常:
public static class Test
{
public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
Contract.Requires(source != null);
Contract.Requires(predicate != null);
foreach (T item in source)
{
if (predicate(item))
{
yield return item;
}
}
}
}
// This throws a contract exception directly, no need of
// enumerating the returned enumerable
Test.Where<string>(null, null);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
214 次 |
| 最近记录: |