Свойства типа набора

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

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>()); } }

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

Кто Вы, предпочитают возвращаться как значение по умолчанию для набора? Если это - пустой набор, как Вы реализуете его?

8
задан Mark Seemann 19 February 2010 в 10:06
поделиться

5 ответов

Из .NET Design Guidelines :

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

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

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

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

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

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

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

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

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

2
ответ дан 6 December 2019 в 00:06
поделиться

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

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

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

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

1
ответ дан 6 December 2019 в 00:06
поделиться

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

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

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

1
ответ дан 6 December 2019 в 00:06
поделиться

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

0
ответ дан 6 December 2019 в 00:06
поделиться

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

private readonly List<MyItemClass> _myList = new List<MyItemClass>();
0
ответ дан 6 December 2019 в 00:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: