2014-01-14 32 views
1

我有一個公共屬性(AllCustomers),這是一個私人財產延遲加載支持。列表與IEnumerable在私人,懶惰加載屬性

我知道公共財產應該是IEnumerable("program to interfaces, not implementations")。

但是,我可以看到兩種方法來構建私有財產。

第一種選擇,與私人列表 -

private List<Customer> _AllCustomers; 
public IEnumerable<Customer> AllCustomers 
{ 
    get 
    { 
     if (_AllCustomers == null) 
     { 
      _AllCustomers = DAL.GetAllCustomers().ToList(); 
     } 
     return _AllCustomers; 
    } 
} 

第二個選項,私人IEnumerable-

private IEnumerable<Customer> _AllCustomers; 
public IEnumerable<Customer> AllCustomers 
{ 
    get 
    { 
     if (_AllCustomers == null) 
     { 
      _AllCustomers = DAL.GetAllCustomers(); 
     } 
     return _AllCustomers; 
    } 
} 

我認爲第一種選擇似乎更正確的,因爲它會打一次數據庫和存儲結果,而第二個將導致多個數據庫命中。

我的問題是 -

  • 上午我在分析是正確的?
  • 不同方法的含義是什麼?
  • 有沒有什麼時間第二種選擇是首選?
  • 是否有更好,更習慣的方式來表達 第二選項?
+1

我們不可能回答不知道什麼'DAL.GetAllCustomers()'一樣。例如,它可能會返回一個'List ',即使它被聲明爲返回IEnumerable 。 –

+0

爲什麼你的私人成員是屬性,而不是字段? – MarcinJuraszek

+0

@MarcinJuraszek - 謝謝,私人會員更新到字段。 – Spongeboy

回答

1

這裏涉及「懶惰」的幾個層次。一種是IEnumerable實現的固有懶惰,另一種是你自己添加到屬性的懶惰實現。

您的第一個實現將命中數據庫一次,第一次訪問AllCustomers。它將構建GetAllCustomers中的查詢,並在調用ToList時執行查詢,並在本地存儲結果。

你的第二個實現將命中數據庫只有一次(假設你的LINQ實現是一半體面)。但是,這將是比第一種情況下 - 即使撥打AllCustomer財產將只返回IQueryable,這將只有當AllCustomers實際訪問或枚舉時纔會執行。這可能是在之後或不是 - 這是一個更懶惰的實現。同樣,假設你的LINQ提供者不是太愚蠢,遍歷整個集合仍然只會觸及DB一次。

爲什麼要選擇第一個選項呢?因爲(再次取決於實現方式),遍歷AllCustomers兩次可能會擊中DB兩次。當我們有可能多次枚舉IEnumerable時,Resharper足夠方便地發出警告。將其存儲在本地List將確保我們保留一個緩存的本地副本。爲了確保在代碼中明確表示,請考慮公開IReadOnlyList而不是IEnumerable

+0

你確定你的陳述'迭代整個集合仍然只會擊中數據庫一次。這絕對是錯誤的,LINQ提供程序返回IQueryable,它在每次枚舉時都碰到DB。 –

+0

@AkashKava你是對的,我不清楚。每次枚舉都會觸發一次數據庫,但不會在枚舉的每次迭代中觸發一次。在這種情況下,我可能誤解了OP的擔憂,但我認爲我的編輯清楚了這一點。 –

+0

即使對於每一次迭代,讀者仍然是開放的。因此,通過查詢來枚舉數據庫連接保持打開狀態。最好調用ToList然後枚舉它。 –

1

由於第一次調用getter時後臺字段僅爲NULL,兩者都只會觸及DB一次。

作爲個人喜好,我通常更喜歡IEnumerable在我的接口和方法聲明中,但是支持字段是具體的類,例如, List<stuff>。使事情更容易修改您的集合等。

+3

不,第二個*可能會在每次請求客戶時觸及數據庫。我們不知道,因爲我們不知道'DAL.GetAllCustomers()'是如何實現的。它可能會返回一個每次都會執行的查詢,或者它可能會返回一個物化列表... –

+1

如果GetAllCustomers返回一個IQueryable,那麼它每次都會打到DB。 –

+0

@AkashKava沒必要。您可以使用'AsQueryable'方法將'List '返回爲'IQueryable '。 – MarcinJuraszek

0

我知道公共屬性應該是IEnumerable(「程序到接口,而不是實現」)。

這種理解是有缺陷的。編程接口的原理並不是說其中接口應該編程。在你的例子中。我會暴露IList<T>,並使其只讀假設你不希望消費者修改列表:

private List<Customer> _AllCustomers; 
public IList<Customer> AllCustomers 
{ 
    get 
    { 
     if (_AllCustomers == null) 
     { 
      _AllCustomers = DAL.GetAllCustomers().ToList().AsReadOnly(); 
     } 
     return _AllCustomers; 
    } 
} 
+0

但是IList比IEnumerable更不容許,「程序接口」原則使用最寬容。我知道如果數據合同意味着需要隨機訪問,應該使用IList。 – Spongeboy

+1

@Spongeboy:我可以看到被方法參數放寬的參數。但是如果我想向調用者暗示執行可能使用懶惰評估,我只會使用IEnumerable的返回值。返回IList 或ICollection 使調用者更容易(Count屬性,更容易重複迭代)。 – Joe