2009-01-13 77 views
7

我經常發現自己寫了一個懶惰評估的房產。例如:實現一個「LazyProperty」類 - 這是個好主意嗎?

if (backingField == null) 
    backingField = SomeOperation(); 
return backingField; 

這不是很多代碼,但如果您有很多屬性,它會重複很多次。

我想定義一個名爲LazyProperty類:

public class LazyProperty<T> 
    { 
    private readonly Func<T> getter; 

    public LazyProperty(Func<T> getter) 
    { 
     this.getter = getter; 
    } 

    private bool loaded = false; 
    private T propertyValue; 

    public T Value 
    { 
     get 
     { 
      if (!loaded) 
      { 
       propertyValue = getter(); 
       loaded = true; 
      } 
      return propertyValue; 
     } 
    } 

    public static implicit operator T(LazyProperty<T> rhs) 
    { 
     return rhs.Value; 
    } 
} 

這將使我能夠初始化一個領域是這樣的:

first = new LazyProperty<HeavyObject>(() => new HeavyObject { MyProperty = Value }); 

然後該財產的身體可以減少到:

public HeavyObject First { get { return first; } } 

這將被大多數公司使用,因爲它會進入我們大部分產品共享的通用類庫。

我不能決定這是不是一個好主意。我認爲解決方案有一些優點,如:

  • 更少的代碼
  • 更漂亮的代碼

不利的一面,這將是很難看的代碼,並確定到底發生了什麼 - 特別是如果開發人員不熟悉LazyProperty類。

您認爲如何?這是一個好主意,還是應該放棄它? 另外,隱式運算符是一個好主意,或者如果您應該使用此類,您是否更願意明確使用Value屬性?

意見和建議,歡迎:-)

回答

7

只是過於迂腐:

你提出的解決方案,以避免重複代碼:

private LazyProperty<HeavyObject> first = 
    new LazyProperty<HeavyObject>(() => new HeavyObject { MyProperty = Value }); 
public HeavyObject First { 
    get { 
    return first; 
    } 
} 

實際上比代碼更多的字符,你不想重複:

private HeavyObject first; 
public HeavyObject First { 
    get { 
    if (first == null) first = new HeavyObject { MyProperty = Value }; 
    return first; 
    } 
} 

除此之外,我認爲隱式轉換使得代碼很難理解。我不會猜到一個簡單地返回的方法實際上最終會創建一個HeavyObject。我至少已經放棄了隱式轉換並返回first.Value屬性。

+0

你的觀點是關於隱式運算符的。這是我需要意見的事情之一。 – driis 2009-01-13 19:36:42

1

我喜歡這個主意,因爲它是更少的代碼,更優雅,但我會有關,它變得很難看,並告訴這個事實很擔心到底是怎麼回事。我認爲唯一的方法是使用「懶惰」方式設置變量的約定,並在使用它的任何地方發表評論。現在不會有編譯器或任何會執行這些規則的東西,所以仍然是YMMV。

最後,對我來說,這樣的決定歸結爲誰將會看到它以及這些程序員的質量。如果你可以相信你的開發人員正確地使用它並且評論得很好,那就去做吧,但如果沒有的話,你最好用一種容易理解和遵循的方式去做。/my 2cents

2

我更喜歡第一個代碼,因爲a)它是一種常見的屬性模式,我立刻就能理解它,b)您提出的觀點:沒有隱藏的魔法,您必須去看看了解何時何地獲取價值。

1

我不認爲擔心開發商不理解是一個反對做這樣的事情的好論據...

如果你認爲,你不能對別人的不理解你做了什麼恐懼做任何事情

你可以寫在中央存儲庫的教程什麼的,我們在這裏爲這些類型的維基指出

總的來說,我認爲這是一個良好的執行理念(不想展開辯論惰性加載是否是一個好主意或沒有)

0

我喜歡你的解決方案,因爲它是非常聰明的,但我不認爲你通過使用贏得很多。在公共屬性中延遲加載私有字段絕對是可以複製代碼的地方。然而,這總是讓我覺得它是一種使用模式,而不是需要重構到一個共同的地方的代碼。

如果您進行任何序列化,您的方法可能會成爲未來的關注點。另外,最初理解您對自定義類型所做的操作會更加困惑。

總的來說,我讚揚你的嘗試,並感謝它的聰明之處,但會建議你恢復到原來的解決方案,出於上述原因。

4

當然你至少希望LazyPropery<T>是一個值類型,否則你已經爲系統中每個「懶惰加載」屬性添加了內存和GC壓力。

另外,多線程場景呢?考慮兩個線程同時請求屬性。如果沒有鎖定,您可能會創建基礎屬性的兩個實例。爲了避免在常見情況下鎖定,您需要執行雙重檢查的鎖定。

+0

+1鎖定,不要太用力,工作到他的設計雖然 – 2009-01-13 19:09:48

+0

是的,我會propably創建inhertied類來實現相同的模式,但與價值吸氣線程安全。然後,班級消費者可以選擇他是否想要/需要線程安全與否。 – driis 2009-01-13 19:16:22

1

我在這種情況下做的是我創建一個Visual Studio code snippet。我認爲這就是你應該做的。

例如,當我創建ASP.NET控件,我時常有被存儲在ViewState大量的數據,所以我創建了一個代碼片段是這樣的:

public Type Value 
{ 
    get 
    { 
     if(ViewState["key"] == null) 
      ViewState["key"] = someDefaultValue; 
     return (Type)ViewState["key"]; 
    } 
    set{ ViewState["key"] = value; } 
} 

這樣,代碼只需一點點工作即可輕鬆創建(定義類型,鍵,名稱和默認值)。它是可重用的,但是你沒有其他開發人員可能不瞭解的複雜代碼的缺點。

0

就個人而言,我不認爲LazyProperty類提供了足夠的價值來證明使用它,特別是考慮到使用它的值類型的缺點(如Kent提到的)。如果您需要其他功能(如使其成爲多線程),則可以將其作爲ThreadSafeLazyProperty類來證明。

關於隱含屬性,我更喜歡「Value」屬性。這是一個更多的打字,但更清晰的我。

5

不要這樣做。

在一種情況下,通常使用這種惰性初始化屬性是一個有效的設計選擇:當SomeOperation();是一個昂貴的操作(就I/O而言,比如需要數據庫命中或計算)時,確定你通常不需要訪問它。

也就是說,默認情況下,您應該進行急切的初始化,並且當探查器說它是您的瓶頸時,然後將其更改爲延遲初始化。

如果你想要創造這種抽象,那是一種氣味。

0

我認爲這是一個有趣的想法。首先,我會建議你從調用代碼中隱藏懶惰屬性,你不想泄漏到你的領域模型,它是懶惰的。你對隱式運算符的處理如此。

我喜歡如何使用這種方法來處理和抽象鎖定細節。如果你這樣做,那麼我認爲它有價值和優點。如果您確實爲雙鎖模式添加鎖定警告,則很容易出錯。

相關問題