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 方法的问题。