Свойства коллекционного типа - PullRequest
8 голосов
/ 19 февраля 2010

Когда свойство имеет некоторый тип коллекции, например

public IList<MyItemClass> MyList{ get; }

ИМХО, лучше возвращать пустую коллекцию вместо нулевого значения.
Есть много способов реализовать такую ​​функциональность.

public IList<MyItemClass> MyList{ get; private set; }
public MyClass(){
    MyList = new List<MyItemClass>();
}

Этот способ позволяет уменьшить количество полей класса, но вам нужно поместить код в каждый конструктор.

private List<MyItemClass> _myList = new List<MyItemClass>();
public IList<MyItemClass> MyList{ get{ return _myList; } }

Это стандартный способ. Но когда кто-то что-то пишет в ваш код, он может использовать приватное поле вместо свойства, и вы можете получить некоторые ошибки, когда вы преобразуете действие.

private List<MyItemClass> _myList;
public IList<MyItemClass> MyList{ get{ return _myList??(_myList = new List<MyItemClass>()); } }

А это вариант предыдущего способа с ленивой загрузкой.

Что вы предпочитаете возвращать в качестве значения по умолчанию для коллекции? Если это пустая коллекция, как вы ее реализуете?

Ответы [ 5 ]

2 голосов
/ 19 февраля 2010

Из .NET Design Guidelines :

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

Расширение этого принципа и объединение его с Принципом наименьшего сюрприза должно сделать совершенно очевидным, что вы должны всегда возвращать пустую коллекцию вместо нуля.

Зачем возлагать бремя проверки на нулевое значение для вашего вызывающего, если вы можете предоставить разумное, интуитивно понятное значение по умолчанию?

Весь смысл инкапсуляции заключается в выполненииработа в одном месте 1020 *.Это может усложнить реализацию этого конкретного класса, но упрощает использование его API.

В большинстве случаев я реализую коллекцию как инвариант в содержащем типе:

public class MyClass
{
    private readonly List<Foo> foos;

    public class MyClass()
    {
        this.foos = new List<Foo>();
    }
}

Обратите внимание на использование ключевого слова readonly, которое гарантирует, что после установки список никогда не будет заменен или обнулен (но он все еще может быть сброшен).Это защищает список как инвариант класса, что избавляет меня от написания нулевых проверок в остальной части моего кода.

Ленивая загрузка также является допустимой идиомой кодирования, но я использую ее только тогда, когда она мне явно нужна.

1 голос
/ 19 февраля 2010

Я обычно использую вариант, где вы инициализируете список в конструкторе. Это может сделать конструктор более «раздутым», но ответственность конструкторов заключается в создании объекта. Настройка хороших значений по умолчанию полностью соответствует его обязанностям. Кроме того, в качестве бонуса вам не нужно беспокоиться о закрытых полях, что делает код более чистым.

Конечно, рефакторинг действительно становится немного сложнее, но незначительно. Не забывайте, что вы можете связать свои конструкторы (в большинстве случаев, в любом случае), поэтому даже если у вас есть несколько конструкторов, вы сможете написать код инициализатора только один раз.

Не забудьте документировать, что значением по умолчанию является пустая коллекция. И установка поля сбора данных только для чтения является хорошей практикой, если только у вас нет веской причины для его сброса позже.

1 голос
/ 19 февраля 2010

Возвращение null действительно не идеально. Для альтернативы; это зависит от использования. ИМО, эта версия (скопированная из основного поста) является самой простой, но делает все, что нам нужно:

private List<MyItemClass> _myList = new List<MyItemClass>();
public IList<MyItemClass> MyList { get { return _myList; } }

и, как преимущество, он будет работать с XmlSerializer, где-либо с private set не будет работать (ошибка; он видит сеттер, но не замечает его не могу его использовать).

Ленивый подход обычно перебивает IMO, так как в большинстве интересных случаев вы все равно будете заполнять списки.

0 голосов
/ 19 февраля 2010

Я предпочитаю второй вариант, потому что он более очевиден и уменьшает размер конструктора.Но это обычно делают эти поля только для чтения:

private readonly List<MyItemClass> _myList = new List<MyItemClass>();
0 голосов
/ 19 февраля 2010

Вариант 2 (стандартный способ) и избегайте проблем с рефакторингом, проверяя, что ваши юнит-тесты проверяют, что пустой список возвращается, когда вы ожидаете.

...