2009-11-24 57 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