У меня есть вопрос "передового опыта" об ООП в C # (но он вроде относится ко всем языкам).
Подумайте о том, чтобы иметь библиотечный класс с объектом, который должен быть открыт для публики, скажем, через средство доступа к свойству, но мы не хотим, чтобы публика (люди, использующие этот библиотечный класс) изменили его.
class A
{
// Note: List is just example, I am interested in objects in general.
private List<string> _items = new List<string>() { "hello" }
public List<string> Items
{
get
{
// Option A (not read-only), can be modified from outside:
return _items;
// Option B (sort-of read-only):
return new List<string>( _items );
// Option C, must change return type to ReadOnlyCollection<string>
return new ReadOnlyCollection<string>( _items );
}
}
}
Очевидно, что лучшим подходом является «вариант C», но очень немногие объекты имеют вариант ReadOnly (и, конечно, ни один из пользовательских классов не имеет его).
Если бы вы были пользователем класса А, ожидали бы вы изменения
List<string> someList = ( new A() ).Items;
распространять на оригинальный (A) объект? Или можно вернуть клон при условии, что это было написано в комментариях / документации? Я думаю, что такой подход клонирования может привести к довольно трудным для отслеживания ошибкам.
Я помню, что в C ++ мы могли возвращать const-объекты, и вы могли вызывать только методы, помеченные как const. Я думаю, что нет такой функции / шаблон в C #? Если нет, то почему бы им не включить это? Я считаю, что это называется const правильность.
Но опять же, мой главный вопрос о том, «чего бы вы ожидали» или как справиться с вариантом А против Б.
источник
Ответы:
Помимо ответа @ Jesse, который является лучшим решением для коллекций: общая идея состоит в том, чтобы спроектировать ваш открытый интерфейс A таким образом, чтобы он возвращал только
типы значений (например, int, double, struct)
неизменяемые объекты (например, «строка» или пользовательские классы, которые предлагают только методы только для чтения, как и ваш класс А)
(только если это необходимо): копии объектов, как показано в «Варианте B»
IEnumerable<immutable_type_here>
само по себе является неизменным, так что это всего лишь частный случай вышеизложенного.источник
Также обратите внимание, что вы должны программировать для интерфейсов, и в целом то, что вы хотите вернуть из этого свойства, является
IEnumerable<string>
. AList<string>
- это что-то вроде «нет-нет», потому что оно, как правило, изменчиво, и я зашёл так далеко, чтобы сказать, что,ReadOnlyCollection<string>
поскольку разработчикICollection<string>
чувствует, что он нарушает принцип подстановки Лискова.источник
IEnumerable<string>
именно то, что здесь нужно.IReadOnlyList<T>
.IList<T>
)Некоторое время назад я столкнулся с подобной проблемой, и мое решение было ниже, но оно действительно работает, только если вы тоже управляете библиотекой:
Начните с вашего класса
A
Затем выведите из этого интерфейс только с частью get свойств (и любыми методами чтения) и сделайте так, чтобы A реализовал
Тогда, конечно, когда вы хотите использовать версию только для чтения, достаточно простого приведения. Это как раз то, чем вы на самом деле являетесь, но я подумал, что предложу метод, с помощью которого я достиг этого
источник
Для тех, кто находит этот вопрос и все еще интересуется ответом - теперь есть еще один вариант, который выставляет
ImmutableArray<T>
. Это несколько моментов , почему я думаю , что это лучше ,IEnumerable<T>
илиReadOnlyCollection<T>
:IEnumerable<T>
делаетIEnumerable
илиIReadOnlyCollect
он / она не может предположить, что она никогда не изменится (другими словами, нет гарантии, что элементы коллекции не были изменены, а ссылка на эту коллекцию остается неизменной)Также, если APi необходимо уведомить клиента об изменениях в коллекции, я бы выставил событие как отдельный член.
Обратите внимание, что я не говорю, что
IEnumerable<T>
это плохо в целом. Я бы по-прежнему использовал его при передаче коллекции в качестве аргумента или при возвращении лениво инициализированной коллекции (в данном конкретном случае имеет смысл скрывать количество элементов, поскольку оно еще не известно).источник