C#Auto Property - 这种'模式'最佳实践吗?

时间:2009-11-24 12:04:53

标签: c# design-patterns automatic-properties

我似乎在我的代码中使用了这种模式,我知道它不再是一个简单的Autoproperty,因为它会:

  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。这是一个“正常”模式\正确的方法吗?

我找不到重复的问题

10 个答案:

答案 0 :(得分:32)

这是我自己经常使用的一种技术。这也可以帮助节省内存资源,因为它没有实例化List&lt;&gt; object,除非在使用代码中实际使用了objects属性。这使用了“延迟加载”技术。

此外,您列出的“延迟加载”技术不是线程安全。如果碰巧同时向该媒体资源发出多个电话,您最终可能会有多次通话,将该媒体资源设置为新的列表&lt;&gt;对象,因此使用新的空List&lt;&gt;覆盖任何现有的List值。宾语。要使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;
    }
}

但是,如果你总是需要List&lt;&gt;对象实例化它只是在对象构造函数中创建它并使用自动属性更简单。如下所示:

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

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

此外,如果您将“set”访问者公开,那么使用代码将能够将该属性设置为Null,这可能会破坏其他消费代码。因此,保持消费代码无法将属性值设置为Null的好方法是将set访问器设置为私有。像这样:

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

相关技术是返回IEnumerable&lt;&gt;来自财产的对象。这样您就可以替换List&lt;&gt;在任何时候在对象内部键入,消费代码不会受到影响。要返回IEnumerable&lt;&gt;你可以只返回普通列表&lt;&gt;直接对象,因为它实现了IEnumerable&lt;&gt;接口。如下所示:

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

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

答案 1 :(得分:10)

你的模式是一个完全合理的延迟加载模式,但要注意它是不是线程安全的

如果两个线程第一次非常接近地访问了这个属性,那么你的空检查模式不会阻止一个线程评估为null但之前它有机会初始化列表的条件,第二个线程也计算为null。在这种情况下,他们都会初始化列表。

此外,有可能在属性返回时,一个线程将拥有该列表的一个副本,而第二个线程具有另一个副本。

在单线程环境中不是问题,但绝对需要注意。

答案 2 :(得分:5)

只要你想要的是正确的模式:

  • 允许外部代码替换整个列表(instance.BCSFilters = null)
  • 在阅读时神奇地创建列表。虽然它很棘手,因为你允许用户将它设置为null(在集合中),但是你不允许它保持为null(因为稍后的get将产生一个空列表)。

您还可以希望以只读模式公开IList(如果需要,使用lazy init),因此用户只能在其中添加或删除项目,而无法覆盖列表本身。如果您有很多分配,可能会涉及复制。

通常我只对我的IList成员有一个getter,我甚至可以暴露IEnumerable或在get中返回一个副本(但是你必须提供特定的Add和Remove方法,所以YMMV)

答案 3 :(得分:5)

仅供参考,一个更简洁的方式来完成您已经在做的完全相同的事情可能看起来像这样:

private IList<BCSFilter> _BCSFilters;

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

答案 4 :(得分:3)

你的方法是

的懒惰初始版本
public class xyz
{
    public xyz()
    {
        BCSFilters = new List<BCSFilter>();
    }

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

答案 5 :(得分:3)

还有另一招:)

从.Net 4使用Lazy。

我在Mark Seemann的博客中看到了这一点,我认为:

 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枚举获得重载! http://msdn.microsoft.com/en-us/library/system.threading.lazythreadsafetymode.aspx

答案 6 :(得分:2)

这是Lazy Load pattern的一个例子。这是一种公认​​的模式,完全有效。

您可以在C#中使用自动属性,并在构造函数中为属性分配实例。 Lazy Load模式的好处是除非调用它,否则不会初始化该属性。这在初始化很昂贵的情况下非常有用。

我更喜欢使用构造函数初始化的自动属性,因为语法更简洁,输入的次数更少,除非初始化很昂贵,在这种情况下Lazy Load运行良好。

答案 7 :(得分:0)

我们在工作场所使用这种模式。这很方便,因为您可以避免在使用代码中出现可能的空引用异常,并使消费代码更简单。

答案 8 :(得分:0)

是的,那是完全正常的; - )

这种懒惰的创作并不罕见,而且很有道理。唯一需要注意的是,如果你完全引用该字段,你必须要小心。

编辑:但是我必须和Chris和其他人一起去:使用auto属性并在构造函数中初始化集合是一​​个(更好)模式。

答案 9 :(得分:0)

这是一个好的模式。 Autoproperties只是最简单的,也可以说是最常见的属性方案的简写,对于某些情况,使用它是不明智的。你可以做的是改为在构造函数中实例化BCSFilters。这样你可以使用autoproperties,你仍然不必担心空引用异常。