为属性分配集合引用时的最佳实践

时间:2009-04-07 12:09:22

标签: c# properties

我非常注重C ++思维,需要针对特定​​C#问题提供一些指导。我们假设我们有以下类:

public class Foo
{
    private IList<Bar> _bars = new List<Bar>(); // Note IList<> vs List<>.

    public IList<Bar> Bars
    {
        get { return _bars; }
        set
        {
            ...
        }
    }
}

现在,取代...,我倾向于清除_barsAddRange set参数value中的项目,而不是只需将value分配给_bars即可。我看待它的方式是,我希望继续引用value项引用的相同,而不是IList<Bar>引用的实际value。< / p>

这是错误的想法吗?你觉得怎么样?

编辑:经过一些评论后,我意识到我必须补充一点,我希望能够在Foo ctor中使用现有的Bars集合并从该集合中初始化_bars。所以,到目前为止,通过该修订和评论,这感觉更好:

public class Foo
{
    private readonly List<Bar> _bars = new List<Bar>();

    public Foo(IEnumerable<Bar> bars)
    {
        _bars.AddRange(bars);
    }

    public IList<Bar> Bars
    {
        get { return _bars; }
    }
}

它更好吗?

6 个答案:

答案 0 :(得分:8)

如果我们讨论的是业务(域)对象,我会公开IEnumerable<T>以及适合该集合的任何方法,通常是Add,Remove和Clear。我几乎从不公开收藏品的制定者。

答案 1 :(得分:3)

是否有必要使您的Foo实例的Bars属性引用另一个List?

如果没有,那么我宁愿没有酒吧的属性设置器。

如果有必要将Bars属性设置为另一个List实例,那么我想我发现你的setter修改了列表会很奇怪。 我的意思是:当我为该属性分配另一个Bars列表时,我会假设我的Foo实例然后包含我已分配给该属性的列表中可用的Bars。不多也不少。 我会发现很奇怪,突然发现该集合包含更多(或更少)的条...

答案 2 :(得分:3)

根据您提问的推断要求:

public class Foo
{
    private readonly IList<Bar> _bars = new List<Bar>();

    public IList<Bar> Bars
    {
        get { return _bars; }
    }
}

答案 3 :(得分:2)

当然,不需要为Bars属性公开setter。在内部,您持有对集合的引用,Kent建议可以将其标记为Readonly。通过getter,调用者可以使用可用方法(Add,Remove,Clear,AddRange等)对集合执行他们想要的操作,但至关重要的是,他们永远不能更改您对集合对象持有的内部引用。

然后,您可以控制允许的方法。正如Jamie所建议的那样,拥有属性返回类型IEnumerable会导致Bars属性暴露一个只读集合。公开IList表示可以修改集合的内容。酒店的安装人员可以将其打开,让来电者做他们想做的事,而且你不再受控制。

修改

按照上面的问题编辑。这实际上取决于Foo对象的使用方式。

由于您主要关注的是从现有Foo个对象列表中初始化Bar ...

IList<Bar> bars = ...some list of Bars previously constructed...

Foo的最新代码示例强制调用者通过构造函数初始化,但也允许他们通过属性更改集合

Foo foo = new Foo(bars);
...
foo.Bars.Clear();
foo.Bars.AddRange(bars);

当允许通过构造函数初始化对象时,您需要问自己为什么要这样做。是吗......

  1. 为了来电者的方便吗?允许调用代码提供可以随后通过属性更改的值。
  2. 因为您想限制对象的使用方式?强制值(或某些值组合)在对象构造上设置,并在对象的整个生命周期内保持固定。
  3. 您需要问自己 - 您是否希望调用者能够在构建Foo对象后更改Bars集合的内容?

    如果否 - 使Bars属性公开一个只读集合 如果是 - 向Foo对象添加默认构造函数,以便调用者不必提供列表来初始化它。但如果他们通过重载构造函数选择,他们可以选择这样做。

答案 4 :(得分:1)

我对这些属性的setter很好,但我通常不允许空值,因为null很少有空的列表具有不同的商业含义。

set { _bars = value ?? new List<Bar>();}

答案 5 :(得分:0)

我认为从getter返回_bars引用有点狡猾。你真的希望班级的客户能够修改列表的内容吗?

所以我会做以下事情:在构造/设置上复制并返回getter的只读视图。