2013-10-09 41 views
4

例如,考慮一個工具類SerializableList創建通過拉姆達廠家直銷VS對象 「的新類型()」 語法

public class SerializableList : List<ISerializable> 
{ 
    public T Add<T>(T item) where T : ISerializable 
    { 
     base.Add(item); 
     return item; 
    } 

    public T Add<T>(Func<T> factory) where T : ISerializable 
    { 
     var item = factory(); 
     base.Add(item); 
     return item; 
    } 
} 

通常我會使用這樣的:

var serializableList = new SerializableList(); 
var item1 = serializableList.Add(new Class1()); 
var item2 = serializableList.Add(new Class2()); 

我也可以通過因子分解來使用它,如下所示:

var serializableList = new SerializableList(); 
var item1 = serializableList.Add(() => new Class1()); 
var item2 = serializableList.Add(() => new Class2()); 

第二種方法似乎是首選的使用模式,因爲我最近一直注意到SO。它真的如此嗎(以及爲什麼,如果是的話)還是僅僅是品味的問題?

+0

第二種方法使用lambda表達式語法,我認爲這兩種方法之間沒有任何區別 –

+0

我沒有看到將調用傳遞給列表的要點。 – tuespetre

+0

@DoanCuong,我知道'lambda'這個詞會彈出,所以我已經更新了標題以支持它:) – Noseratio

回答

2

緩存

在您提供它沒有任何意義,因爲其他人指出的例子。相反,我會再給你一個例子,

public class MyClass{ 
    public MyClass(string file){ 
     // load a huge file 
     // do lots of computing... 
     // then store results... 
    } 
} 

private ConcurrentDictionary<string,MyClass> Cache = new .... 

public MyClass GetCachedItem(string key){ 
    return Cache.GetOrAdd(key, k => new MyClass(key)); 
} 

在上面的例子中,假設我們正在裝載一個大文件,我們計算的東西,我們感興趣的是計算的最終結果。爲了加速我的訪問速度,當我嘗試通過緩存加載文件時,緩存將返回緩存條目(如果緩存條目有緩存條目),只有當緩存未找到該條目時,它纔會調用Factory方法並創建MyClass的新實例。

因此,您正在多次讀取文件,但您只創建一次只保存一次數據的類的實例。該模式僅用於緩存目的。但是如果你沒有緩存,並且每次迭代都需要調用new操作符,那麼使用工廠模式根本沒有意義。

備用錯誤的對象或錯誤記錄

出於某種原因,如果創建失敗,列表可以創建一個錯誤的對象,例如,

T defaultObject = .... 

public T Add<T>(Func<T> factory) where T : ISerializable 
{ 
    T item; 
    try{ 
     item = factory(); 
    }catch(ex){ 
     Log(ex); 
     item = defaultObject; 
    } 
    base.Add(item); 
    return item; 
} 

在這個例子中,可以監視工廠如果它在創建新對象時會生成一個異常,當發生這種情況時,您會記錄錯誤並返回其他內容並在列表中保留一些默認值。我不知道這會有什麼實際用途,但錯誤記錄在這裏聽起來更好。

3

鑑於你的例子,工廠方法很愚蠢。除非被調用者需要控制實例化的實例,實例化多個實例或懶惰的評估,否則這只是無用的開銷。

編譯器將無法優化委託創建。

要引用在問題的評論中使用出廠語法的示例。這兩個例子都試圖(儘管很糟糕)提供有保證的實例清理。

如果考慮using語句:

using (var x = new Something()) { } 

天真的實現將是:

var x = new Something(); 
try 
{ 
} 
finally 
{ 
    if ((x != null) && (x is IDisposable)) 
    ((IDisposable)x).Dispose(); 
} 

這段代碼的問題是,它是可能的轉讓後,會出現一個異常x,但在輸入try塊之前。如果發生這種情況,x將不會妥善處置,因爲finally塊將不會執行。爲了解決這個問題,對於using語句的代碼實際上是更多的東西一樣:

Something x = null; 
try 
{ 
    x = new Something(); 
} 
finally 
{ 
    if ((x != null) && (x is IDisposable)) 
     ((IDisposable)x).Dispose(); 
} 

,你參考使用出廠參數的例子兩者都試圖解決同樣的問題。傳遞工廠允許實例在保護區塊內實例化。直接傳遞實例允許沿途出錯,並且不會調用Dispose()

在這些情況下,傳遞工廠參數是有道理的。

+0

我同意這一點,但我覺得我仍然可能錯過了一些東西。順便說一句,你爲什麼認爲編譯器沒有優化(或內聯)這些簡單的lambda表達式,你是否檢查過發佈版本的IL代碼(我沒有)? – Noseratio

+1

我沒有特別檢查發佈版本,但是我在調​​試版本中進行了驗證。編譯器無法優化委託,因爲被調用方法需要委託。你不會錯過任何東西,每種技巧都能在正確的場景中達到目的。傳遞工廠只是一個非常特殊的情況。 – William

+0

關於示例中'using'的「天真」實現,是不是編譯器實現它的確切方式:http://msdn.microsoft.com/en-us/library/yh598w02.aspx? 'new'語句被放置在'try'塊之外。 – Noseratio

1

不,沒有通過工廠而不是價值的一般偏好。但是,在非常特殊的情況下,您寧願傳遞工廠方法,而不是值。

想一想:

什麼傳遞參數作爲一個值,或者 將它作爲一個工廠方法(例如,使用Func<T>)之間的差?

答案很簡單:訂單執行的。

  • 在第一種情況下,您需要傳遞該值,因此您必須在調用目標方法之前獲取該值。
  • 在第二種情況下,您可以推遲值創建/計算/獲取,直到目標方法需要爲止。

爲什麼要延期創建/計算/獲取價值?明顯的事情浮現在腦海中:

  • 處理器密集型或內存密集型的創造價值的,要只發生的情況下,該值真的需要(點播)。這是然後延遲加載
  • 如果創建值取決於目標方法可訪問的參數,而不是來自外部的參數。所以,你會通過Func<T, T>而不是Func<T>
1

這個問題比較不同目的的方法。第二個應該命名爲CreateAnd Add <T>(Func<T> factory)

因此,取決於需要什麼功能,應該使用一種或另一種方法。