2011-09-27 65 views
45

我最近開始使用Lazy在我的應用程序,如果有,我需要使用Lazy<T>時考慮任何明顯的消極方面,我想知道?懶惰的缺點<T>?

我試圖利用Lazy<T>經常因爲我認爲合適,主要是爲了幫助減少我們裝的內存空間,但不活躍的插件。

+4

我剛開始使用懶惰,發現它通常表示設計不好;或程序員的懶惰。另外,一個缺點是你必須對範圍變量保持警惕,並創建適當的關閉。 – Gleno

+4

@Gleno爲什麼這個程序員的懶惰? –

+4

@Gleno,Anton:更重要的是,它爲什麼不好?我在編程課上總是教導懶惰是程序員的一個重要優點。 –

回答

17

我會擴大我的評論,其內容有點:

我用懶惰剛剛起步,並發現它的常反映不好的設計 ;或程序員的懶惰。此外,一個 缺點是,你必須與作用域起來 變量更加警惕,並建立適當的關閉。

例如,我使用Lazy<T>創建的頁面,用戶可以在我的(無會話)MVC應用中看到。這是一個引導向導,因此用戶可能想要隨機前往步驟步驟。當握手進行時,一系列Lazy<Page>對象被打包,如果用戶指定爲步驟,則評估該確切頁面。我覺得它提供了不錯的表現,但也有一些方面它,我不喜歡,比如我的很多foreach結構現在這個樣子:

foreach(var something in somethings){ 
    var somethingClosure = something; 
    list.Add(new Lazy<Page>(() => new Page(somethingClosure)); 
} 

即你必須非常積極地處理關閉問題。否則,我不認爲這是一個糟糕的性能打擊存儲一個lambda並在需要時評估它。

另一方面,這可能表示程序員是Lazy<Programmer>,因爲您現在不想考慮您的程序,而是在需要時讓適當的邏輯進行評估,例如在我的例子中情況 - 而不是建立這個數組,我只能弄清楚那個特定的請求頁面是什麼;但我選擇了懶惰,並採取全方位的做法。

編輯

它發生,我認爲Lazy<T>併發工作時,也有一些peculiars。例如,對於某些場景,有一個ThreadLocal<T>,以及針對您的特定多線程場景的多個標誌配置。你可以閱讀更多關於msdn

+3

這不是'懶惰'本身的問題。相反,這是你如何使用它。 –

+3

@安頓,是的;我的猜想是Lazy <>有時會給你提供不尋找更好解決方案的問題。鑑於這種選擇,你可能會解決一些剛剛起作用的問題。 – Gleno

+0

@富士,讓我們這樣說吧 - 可能發生的最糟糕的情況是,你突然需要評估所有懶惰物體,因爲規格的變化,或者面臨重大改寫。你能忍受嗎? – Gleno

4

與其他任何東西一樣,Lazy<T>可用於善意或邪惡,因此是一個缺點:如果使用不當,可能會導致混淆和沮喪。然而,懶惰的初始化模式已經存在多年了,現在.NET BCL有了一個實現,開發人員不需要再次重新發明輪子。更何況,MEF loves Lazy

2

究竟你的意思「在我的應用程序」什麼?

我認爲,當你不知道,如果該值將被使用或沒有,這可能僅僅是與需要很長的時間來計算可選參數的情況下,它應該只被使用。這可能包括複雜的計算,文件處理,Web服務,數據庫訪問等等。

在另一方面,爲什麼要用Lazy這裏?在大多數情況下,你可以簡單地調用一個方法而不是lazy.Value,反正也沒有區別。但是對於程序員來說更簡單明瞭的是在這種情況下沒有Lazy

一個明顯的上攻可能已經實現了價值的緩存,但我不認爲這是一個大的優勢。

7

這不是一個負面的方面,但對懶惰的人來說是一個陷阱:)。

延遲初始化程序就像靜態初始化程序。他們運行一次。如果引發異常,則緩存該異常,隨後對.Value的調用將拋出相同的異常。這是設計並在文檔中提到... http://msdn.microsoft.com/en-us/library/dd642329.aspx

緩存由valueFactory引發的異常。

因此,代碼如下絕不會返回一個值:

bool firstTime = true; 
Lazy<int> lazyInt = new Lazy<int>(() => 
{ 
    if (firstTime) 
    { 
     firstTime = false; 
     throw new Exception("Always throws exception the very first time."); 
    } 

    return 21; 
}); 

int? val = null; 
while (val == null) 
{ 
    try 
    { 
     val = lazyInt.Value; 
    } 
    catch 
    { 

    } 
} 
+0

謝謝@ Thilak。這很有趣。我不知道異常是像這樣緩存的。 – eandersson

+1

@Fuji這就是爲什麼MEF團隊在MEF 2中添加了ExportFactory和ExportLifetimeContext。請看http://blogs.msdn.com/b/bclteam/archive/2011/11/17/exportfactory-amp-lt-t -amp-gt-in-mef-2-alok.aspx –

+0

我想我現在需要回去修改一些MEF代碼。;) – eandersson

7

在我看來,你應該總是有選擇懶惰的理由。根據使用情況有幾種選擇,肯定有這種結構適合的情況。但不要僅僅因爲它很酷就使用它。

例如我不明白在頁面選擇示例中的一點在其他的答案之一。使用Lazy列表選擇單個元素可以直接使用委託的列表或字典,而無需使用Lazy或簡單的switch語句。

所以最明顯的替代品

  • 直接實例廉價的數據結構或者是無論如何需要
  • 代表們所需要的零到幾十倍的一些算法
  • 一些緩存結構的東西結構對於應該釋放內存時不使用一段時間
  • 某種類似任務「未來」的結構已經可以開始實際使用之前異步初始化消耗的情況下wher CPU空閒時間的項目E中的概率是相當高的,該結構將在

與此相反以後需要,懶惰是常合適的當需要零到在多次

  • 計算密集的數據結構
  • 一些算法,其中零的情況下有顯著概率
  • 和數據是本地的一些方法或類,並可以進行垃圾回收時不使用任何更多的或數據應保存在內存中的整個程序的運行時
5

我來使用Lazy<T>主要是因爲它是在加載資源的併發能力,從數據庫中。因此我擺脫了鎖定對象和可爭論的鎖定模式。 在我的情況ConcurrentDictionary + Lazy作爲一種價值讓我很快樂,感謝@Reed科普塞和他blog post

這看起來像下面這樣。而不是調用的:

MyValue value = dictionary.GetOrAdd(
          key, 
          () => new MyValue(key)); 

我們將改用ConcurrentDictionary>和 寫:

MyValue value = dictionary.GetOrAdd(
          key, 
          () => new Lazy<MyValue>(
           () => new MyValue(key))) 
          .Value; 

Lazy<T>沒有缺點注意到至今。

+0

這很漂亮。不幸的是,我今天過於活躍,跑出了票數,所以我無法對您的答案進行投票。 ; *( – eandersson

2

懶惰是用來保存資源,而不是真的需要。這種模式非常好,但實現可能無用。

資源越大,有用就是這種模式。

使用懶惰類的缺點是使用不透明。事實上,你必須在任何地方維護一個額外的間接(.Value)。 當你只需要一個真實類型的實例時,即使你不需要直接使用它,它也會被強制加載。

懶惰是懶惰的發展獲得生產力,但這種收益可以通過高使用率損失。

如果你有一個真正的透明實現(例如使用代理模式),它可以消除不利情況,在許多情況下它可能非常有用。

併發性必須在其他方面考慮,並且默認情況下不會在您的類型中實現。它只能包含在客戶端代碼或類型助手中。