Лучшая практика при назначении ссылки на коллекцию для свойства - PullRequest
2 голосов
/ 07 апреля 2009

Я в значительной степени ориентирован на мышление C ++ и нуждаюсь в некотором руководстве по конкретному вопросу C #. Предположим, у нас есть следующий класс:

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

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

Теперь вместо ... я склоняюсь к очистке _bars и AddRange элементов из аргумента set value вместо того, чтобы просто присваивать value _bars. На мой взгляд, я хочу продолжать ссылаться на те же элементы , на которые ссылается value элементы, а не на фактические IList<Bar>, на которые value ссылается.

Это неправильное мышление на моей стороне? Как вы думаете, здесь?

Редактировать: После некоторых комментариев я понял, что должен добавить, что я хочу иметь возможность использовать существующую коллекцию Bars в Foo Ctor и инициализировать _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 ]

8 голосов
/ 07 апреля 2009

Если мы говорим о бизнес-объектах (доменах), я раскрываю IEnumerable<T> и любые методы, подходящие для коллекции, как правило, Add, Remove и Clear. Я почти никогда не выставляю сеттер для коллекции.

3 голосов
/ 07 апреля 2009

На основании предполагаемых требований из вашего вопроса:

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

    public IList<Bar> Bars
    {
        get { return _bars; }
    }
}
3 голосов
/ 07 апреля 2009

Обязательно ли иметь возможность заставить свойство Bars вашего экземпляра Foo ссылаться на другой список?

Если нет, то я бы предпочел не устанавливать свойства для Bars.

Если необходимо иметь возможность установить свойство Bars для другого экземпляра List, то я думаю, что было бы странно обнаружить, что ваш установщик изменяет список. Я имею в виду: когда я назначаю этому списку другой список столбцов, я предполагаю, что мой экземпляр Foo содержит столбцы, которые были доступны в списке, который я назначил этому свойству. Не больше, не меньше. Мне было бы странно узнать, что вдруг в коллекции больше (или меньше) баров ...

2 голосов
/ 07 апреля 2009

Безусловно, нет необходимости выставлять установщик для свойства Bars. Внутренне у вас есть ссылка на коллекцию, которая, как предполагает Кент, может быть помечена как Readonly. Через получатель вызывающая сторона может делать с коллекцией то, что она хочет, с помощью доступных методов (Add, Remove, Clear, AddRange и т. Д.), Но, что самое важное, они никогда не смогут изменить внутреннюю ссылку, которую вы держите на объект коллекции.

Это позволяет вам контролировать, какие методы разрешены. Как предполагает Джейми, наличие возвращаемого свойства 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. потому что вы хотите ограничить использование объекта? Принудительное задание значений (или определенных комбинаций значений) при построении объекта и сохранение их в течение всего срока службы объекта.

Вы должны спросить себя - хотите ли вы, чтобы вызывающая сторона могла изменять содержимое коллекции Bars после создания объекта Foo?

Если нет - сделать свойство Bars доступным только для чтения.
Если да - добавить конструктор по умолчанию к объекту Foo, чтобы вызывающая сторона не предоставляла список для его инициализации. Но у них будет возможность сделать это, если они захотят через перегруженный конструктор.

1 голос
/ 08 апреля 2009

Я в порядке с установщиками для таких свойств, но я обычно запрещаю нули, потому что ноль редко имеет другое деловое значение, чем пустой список.

set { _bars = value ?? new List<Bar>();}
0 голосов
/ 08 апреля 2009

Я думаю, что возвращать ссылку _bars из геттера немного хитро. Вы действительно хотите, чтобы клиенты этого класса могли изменять содержимое списка?

Таким образом, я бы сделал следующее: сделаю копию при построении / настройке и верну для чтения метод только для чтения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...