C# 本地函数与yield语句
原创
©著作权归作者所有:来自51CTO博客作者biyusr216的原创作品,请联系作者获取转载授权,否则将追究法律责任
Where 方法的一个简单实现,下面将其添加 Where1 方法的实现中,检查 source和 predicate 参数是否为null:
public static IEnumerable<T> Wherel<T>(this IEnumerable<T> source, Func<T,bool> predicate){ if (source == null) throw new ArgumentNullException(nameof(source)); if (predicate == null) throw new ArgumentNullException(nameof(predicate));
foreach (T item in source) { if (predicate(item)) { yield return item; } }}
编写代码测试 ArgumentNullException,定义预处理语句 #line,以从源代码行 1000开始。在第 1004 行中没有发生异常,其中 null 传递给 Where1 方法;相反,在第1006 行中包含的 foreach 语句发生了异常。延迟发现这个的原因是,在方法 Where1的实现中,延迟执行了 yield 语句。
private static void YieldSampleSimple(){#line 1000 Console.WriteLine(nameof(YieldSampleSimple)); try { string[] names = { "James", "Niki", "John", "Gerhard", "Jack" }; var q = names.Wherel(null); foreach (var n in q) // callstack position for exception { Console.WriteLine(n); } } catch (ArgumentNullException ex) { Console.WriteLine(ex); } Console.WriteLine();}
为了解决这个问题,并向调用者更早地提供错误信息,Where1 方法通过 Where2 方法在两个部分实现。这里,Where2 方法只检查不正确的参数,不包括 yield 语句。使用 yield return 的实现是在一个单独的私有方法 WhereImpl 中完成的。在检查输入参数之后,从 Where2 方法中调用此方法。
public static IEnumerable<T> Where2<T>(this IEnumerable<T> source, Func<T, bool> predicate){ if (source == null) throw new ArgumentNullException(nameof(source)); if (predicate == null) throw new ArgumentNullException(nameof(predicate)); return Where2Impl(source, predicate);}private static IEnumerable<T> Where2Impl<T>(IEnumerable<T> source, Func<T, bool> predicate){ foreach (T item in source) { if (peedicate(item)) { yield return item; } }}
现在调用该方法,堆栈跟踪显示在第 1004 行中发生的错误,其中调用了 Where2方法:
private static void YieldSampleWithPrivateMethod() {#line 1000 Console.WriteLine(nameof(YieldSampleWithPrivateMethod)); try { string[] names = { "James", "Niki", "John", "Gerhard", "Jack" }; var q = names.Where2(mull); // callstack position for erception foreach (var n in q) { Console.WriteLine(n); }}catch (ArgumentNullException ex){ Console.WriteLine(ex); } Console.WriteLine();}
这个问题是用 Where2 方法解决的。但是,现在有了一个仅需要在一个地方使用的私有方法。Where2 方法的主体包括参数检查和 Where2Impl 方法的调用。对于私有方法来说,这是一个很好的场景。Where3 方法的实现包括对输入参数的检查(与以前一样),以及一个私有函数,而不是以前的私有方法 Where2Impl。本地函数可以有更简单的签名,因为它可以从外部作用域访问变量的源和谓词:
public static IEnumerable<T> Where3<T>(this IEnumerable<T> source, Func<T,bool> predicate){ if (source == null) throw new ArgumentNullException (nameof (source)); if (predicate == null) throw new ArgumentNullException (nameof (predicate)); return Iterator(); IEnumerable<T> Iterator() { foreach (T item in source) { if (predicate(item)) { yield return item; } } }}
调用 Where3 方法,其结果与调用 Where2 方法的结果相同。堆栈跟踪显示了调用 Where3 方法的问题。