2010-02-12 58 views
3

我不能做我想做的事。我只想要非国际代表的帐户。但是当我调用ActiveAccounts()时,我没有变为空,我得到一个可枚举的,然后包含null。我在这里做错了什么?请帮助。为什么这种扩展方法不起作用?

public static class AccountExt 
{ 
    public static IEnumerable<Account> ActiveAccounts(this AccountRep rep) 
    { 
     if(rep == null) 
      throw new ArgumentNullException(); 
     if(rep.IsInternational) 
      yield return null; 

     foreach(var acc in rep.FetchAccounts()) 
     { 
      if(acc.IsActive) 
       yield return acc; 
     } 
    } 
} 

回答

8

好吧,这里有一些事情正在进行。

首先,你不只是有一个扩展方法,你有一个扩展方法迭代器块 - 这就是你得到什么,当你使用yield return来自动实现IEnumerable<>合同。

这听起来像你想要发生的是ActiveAccounts()返回null IEnumerable<Account>。实际发生的事情是,对于国际代表,您将作为IEnumerable的第一个元素返回空值。我怀疑你可能已经 使用return null有试过,但有一个编译器错误是这样的:

Error: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.

如果你打算对于枚举为空,你要的是yield break而不是yield return null。通常,返回一个空序列实际上是一个更好的主意,因为它允许调用者避免检查返回值。它还使用像LINQ这样的技术来运行,它使用合成来组装复杂的查询。

第二个问题是,当您调用ActiveAccounts()时,而不是在您开始枚举该调用的结果时,不会评估if(rep == null)前提条件。这可能不是你想要的 - 我想象你想要立即评估先决条件。

您解决这两个问题的方法是使用两阶段实施:

public static class AccountExt 
{ 
    // apply preconditions, return null for international reps 
    public static IEnumerable<Account> ActiveAccounts(this AccountRep rep) 
    { 
     if(rep == null) 
      throw new ArgumentNullException("rep"); 
     if(rep.IsInternational) 
      return null; 
     // otherwise... 
     return ActiveAccountsImpl(rep); 
    } 

    // private implementation handles returning active accounts 
    private static IEnumerable<Account> ActiveAccountsImpl(AccountRep rep) 
    { 
     foreach(acc in rep.FetchAccounts()) 
     { 
      if(acc.IsActive) 
       yield return acc; 
     } 
    } 
} 

如果你愿意使用LINQ,以可避免Impl版的功能:

public static IEnumerable<Account> ActiveAccounts(this AccountRep rep) 
    { 
     if(rep == null) 
      throw new ArgumentNullException("rep"); 
     if(rep.IsInternational) 
      return null; 
     // otherwise, using LINQ to filter the accounts... 
     return rep.FetchAccounts().Where(acc => acc.IsActive); 
    } 

您可以详细了解迭代器如何阻止here

5

您应该用yield break替换yield return null。这将返回一个空序列。

如果你真的想要返回null而不是IEnumerable那么LBushkin的答案是你想要的;但是,返回一个空序列更为常见,因为它不要求消费者必须检查返回值。

+0

没错。我讨厌它,当返回任何类型的枚举的函数返回null。它的代码混乱。 – 2010-02-12 00:26:55

+0

即使有'yield break',OP也可能希望将该方法拆分为两个,以便前提条件可以立即进行计算,而不必在枚举器迭代时进行计算。这可能会导致意外的异常远离实际的故障点。 – LBushkin 2010-02-12 00:29:22

+0

我需要ActiveAccounts()来返回null。空的可扩展性不是我需要的,所以会产生突破性工作? – msophie 2010-02-12 00:31:59

相关问题