2009-11-24 64 views
9

我似乎可以用這種在我的代碼有很多的模式,我知道這是不是一個簡單的Autoproperty任何更因爲這將是:C#自動屬性 ​​- 這是'模式'的最佳實踐嗎?

public IList<BCSFilter> BCSFilters { get; set; } 

我一直在使用的代碼是這樣的:

private IList<BCSFilter> _BCSFilters; 

    /// <summary> 
    /// Gets or sets the BCS filters. 
    /// </summary> 
    /// <value>The BCS filters.</value> 
    public IList<BCSFilter> BCSFilters 
    { 
     get 
     { 
      if (_BCSFilters == null) 
      { 
       _BCSFilters = new List<BCSFilter>(); 
      } 

      return _BCSFilters; 
     } 
     set 
     { 
      _BCSFilters = value; 
     } 
    } 

這樣我就可以做MainClass.BCSFilters,而不用擔心需要在消費代碼中實例化List。這是一個'正常'模式\正確的方式來做到這一點?

我找不到重複的問題

回答

31

的延遲初始化版本,這是我用了很多自己的技術。這還可以幫助節省內存資源,因爲它沒有實例化列表對象,除非對象屬性實際上在消費代碼中被使用。這使用「延遲加載」技術。

此外,您列出的「延遲加載」技術不是線程安全的。如果同時發生多個呼叫,則可能會有多次呼叫將該屬性設置爲一個新的列表對象,從而會用新的空列表對象覆蓋任何現有的列表值。爲了使Get訪問線程安全的,你需要使用Lock statement,就像這樣:

private IList<BCSFilter> _BCSFilters; 

// Create out "key" to use for locking 
private object _BCSFiltersLOCK = new Object(); 

/// <summary> 
/// Gets or sets the BCS filters. 
/// </summary> 
/// <value>The BCS filters.</value> 
public IList<BCSFilter> BCSFilters 
{ 
    get 
    { 
     if (_BCSFilters == null) 
     { 
      // Lock the object before modifying it, so other 
      // simultaneous calls don't step on each other 
      lock(_BCSFiltersLOCK) 
      { 
       if (_BCSFilters == null) 
       } 
        _BCSFilters = new List<BCSFilter>(); 
       } 
      } 
     } 

     return _BCSFilters; 
    } 
    set 
    { 
     _BCSFilters = value; 
    } 
} 

不過,如果你總是需要的清單<>對象實例化這是一個稍微簡單對象的構造函數中只是創建它並改用自動屬性。像下面這樣:

public class MyObject 
{ 
    public MyObject() 
    { 
     BCSFilters = new List<BCSFilter>(); 
    } 

    public IList<BCSFilter> BCSFilters { get; set; } 
} 

此外,如果您離開「設置」存取公共那麼消費代碼就能將該屬性設置爲空,可破壞其他消費代碼。因此,讓消費代碼無法將屬性值設置爲Null的一種好方法是將set訪問器設置爲private。像這樣:

public IList<BCSFilter> BCSFilters { get; private set; } 

相關的技術是從屬性返回一個IEnumerable對象。這將允許您隨時在對象內部替換列表<>類型,並且消費代碼不會受到影響。要返回IEnumerable> <>,您可以直接返回普通列表<>對象,因爲它實現了IEnumerable接口。像下面這樣:

public class MyObject 
{ 
    public MyObject() 
    { 
     BCSFilters = new List<BCSFilter>(); 
    } 

    public IEnumerable<BCSFilter> BCSFilters { get; set; } 
} 
+0

+1同意。不要在沒有必要的地方添加它。 – 2009-11-24 12:11:39

+0

感謝Chris給出了一個簡明扼要的答案 – 2009-11-24 12:20:01

+0

唯一的缺點是這個初始化不是線程安全的,另請參閱Rob Levine的回覆。 (如果你把這個寫入你的回覆中,會很好。) – peterchen 2009-11-24 12:40:27

0

我們在我的工作場所使用該模式。這很方便,因爲您可以避免使用代碼中的可能的空引用異常,並且使代碼更簡單。

3

你的做法是

public class xyz 
{ 
    public xyz() 
    { 
     BCSFilters = new List<BCSFilter>(); 
    } 

    public IList<BCSFilter> BCSFilters { get; set; } 
} 
0

是啊,這是完全正常的;-)

這樣的懶人創造的情況並不少見,而且很有道理。唯一需要注意的是,如果你參考該領域,你必須小心。

編輯:但我必須去與克里斯和其他人:這是一個(多)更好的模式使用自動屬性和在構造函數中初始化集合。

0

這是一個好的模式。 Autoproperties只是最簡單的,也是最常見的屬性的簡寫,對於一些場景,使用它並不明智。你可以做的是,在構造函數中實例化BCSFilters。這樣你可以使用自動屬性,你仍然不必擔心空引用異常。

5

它只要是正確的模式,你想要的是:

  • 允許外部的代碼替換整個列表(instance.BCSFilters = NULL)
  • 對讀奇蹟般地創建的列表。儘管如此,它仍然很棘手,因爲您允許用戶將其設置爲null(在集合中),但是您不允許它保持爲空(因爲稍後獲取將產生一個空列表)。

您還可以以只讀模式公開IList(如果需要,使用惰性初始化),因此用戶只能在其中添加或刪除項目而不能覆蓋列表本身。如果你有很多分配,可能會涉及複製。

通常我只有一個getter我IList的成員,我甚至可能會暴露的IEnumerable或GET返回副本(但你必須provid具體的添加和刪除方法,所以因人而異)

+0

+1 - 懶惰地實例化一個列表是好的,但是沒有很多好的理由允許你的類之外的代碼替換你的整個列表實例。 – 2009-11-24 22:01:31

2

這是Lazy Load pattern的示例。這是一種可接受的模式,非常有效。

您可以在C#中使用自動屬性並將實例賦值給構造函數中的屬性。延遲加載模式的好處是,除非被調用,否則不要初始化屬性。這在初始化昂貴的情況下非常有用。

我傾向於更喜歡使用構造函數初始化的自動屬性,因爲語法更簡潔,並且輸入的次數更少除非初始化非常昂貴,在這種情況下,Lazy Load運行良好。

+0

感謝Dariom的鏈接,我認爲它一定是某種模式...現在我知道這是懶惰加載 – 2009-11-24 12:30:21

10

您的圖案是一個完全合理的延遲加載模式,但請注意,它不是線程安全的

如果兩個線程訪問該屬性的第一次非常接近,你的空檢查模式將不會阻止其中一個線程的計算結果爲空,但纔得到一個機會來初始化列表中的狀態,第二個線程也評估爲null。在這種情況下,他們都會初始化列表。

此外,在屬性返回時,一個線程將有一個列表副本,而另一個線程有另一個副本。

在單線程環境中不存在問題,但絕對值得注意。

+0

感謝羅布,我們目前不使用多線程,但我同意如果我們可以考慮這些事情作爲儘可能早地它會在以後節省心痛 – 2009-11-24 12:40:20

5

僅供參考,做你已經做同樣的事情的一個較爲簡潔的方式可能是這樣的:

private IList<BCSFilter> _BCSFilters; 

public IList<BCSFilter> BCSFilters 
{ 
    get 
    { 
     return _BCSFilters ?? (_BCSFilters = new List<BCSFilter>()); 
    } 
    set 
    { 
     _BCSFilters = value; 
    } 
} 
+0

哦,是的。合併操作員大大簡化了事情。 +1 – 2010-11-17 12:42:02

3

有另一種伎倆:)

從.NET 4中使用延遲。

我在馬克塞曼博客看到了這一點,我認爲:

public class Order 
{ 
    public Order() 
    { 
     _customerInitializer = new Lazy<Customer>(() => new Customer()); 
    } 

    // other properties 

    private Lazy<Customer> _customerInitializer; 
    public Customer Customer 
    { 
     get 
     { 
      return _customerInitializer.Value; 
     } 
    } 

    public string PrintLabel() 
    { 
     string result = Customer.CompanyName; // ok to access Customer 
     return result + "\n" + _customerInitializer.Value.Address; // ok to access via .Value 
    } 
} 

通知日在「_customerInitializer」永遠不能爲null,所以使用它是非常相同的。 它也可以是線程安全! 構造函數可以通過LazyThreadSafetyMode Enum獲得重載! http://msdn.microsoft.com/en-us/library/system.threading.lazythreadsafetymode.aspx