2010-02-12 84 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

相關問題