C#本地函数与yield语句

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