Поскольку вопрос был написан, в .NET 4.0 добавлен интерфейс IReadOnlyCollection<T>
; вероятно, было бы хорошо использовать это как объявленный тип возвращаемого значения.
Это, однако, оставляет открытым вопрос о том, какой тип экземпляра вернуть. Один из подходов заключается в клонировании всех элементов в исходной коллекции. Другой вариант - всегда возвращать оболочку только для чтения. Третье - вернуть оригинальную коллекцию, если она реализует IReadOnlyCollection<T>
. Каждый подход будет лучшим в определенных контекстах, но будет не идеальным (или, возможно, совершенно ужасным) в других. К сожалению, Microsoft не предоставляет стандартных средств, с помощью которых можно задать два очень важных вопроса:
Обещаете ли вы всегда и навсегда содержать те же предметы, что и сейчас?
Можете ли вы безопасно подвергнуться воздействию кода, который не должен изменять ваше содержимое.
Какой стиль упаковки подходит, будет зависеть от того, что клиентский код ожидает от того, что он получает. Некоторые сценарии, которых следует избегать:
Был предоставлен объект типа, который клиент распознает как неизменяемый, но вместо того, чтобы быть возвращенным напрямую, он дублируется, используя тип, который клиент не распознает как неизменяемый. Следовательно, клиент вынужден снова дублировать коллекцию.
Был предоставлен объект такого типа, который клиент распознал бы как неизменяемый, но перед возвращением он упакован таким образом, что клиент не может определить, является ли коллекция неизменной или нет, и поэтому является вынужден дублировать.
Объект изменяемого типа, который не должен быть видоизмененным, предоставляется клиентом (приведен к интерфейсу только для чтения). Затем он напрямую предоставляется другому клиенту, который определяет, что это изменчивый тип, и приступает к его изменению.
Получена ссылка на изменяемую коллекцию, которая инкапсулируется в оболочку только для чтения, а затем возвращается клиенту, которому нужен неизменяемый объект. Метод, который возвратил коллекцию, обещал, что она неизменна, и поэтому клиент отказался сделать свою собственную защитную копию. Затем клиент плохо подготовлен к тому, что коллекция может измениться.
На самом деле не существует какого-либо особенно "безопасного" действия, которое объект может предпринять с коллекциями, которые он получает от одних клиентов и нуждается в предоставлении другим. Всегда дублирование всего - во многих случаях самый безопасный способ действий, но это может легко привести к ситуациям, когда коллекция, которую не нужно дублировать вообще, заканчивается дублированием сотни или тысячи раз. Возврат ссылок в полученном виде часто может быть наиболее эффективным подходом, но он также может быть семантически опасным.
Хотелось бы, чтобы Microsoft добавила стандартное средство, с помощью которого коллекции могли бы задавать вышеуказанные вопросы или, еще лучше, попросить сделать неизменный снимок их текущего состояния. Неизменяемая коллекция может очень дешево вернуть неизменяемый снимок своего текущего состояния (просто вернуть себя), и даже некоторые изменяемые типы коллекций могут вернуть неизменяемый снимок по цене, намного меньшей стоимости полного перечисления (например, List<T>
может быть зарезервировано двумя массивами T[][]
, один из которых содержит ссылки на неизменяемые неизменяемые массивы из 256 элементов, а другой - ссылки на неразделимые изменяемые массивы из 256 элементов. Создание снимка списка потребует клонирования только внутренних массивов, содержащих элементы которые были изменены с момента последнего снимка - потенциально намного дешевле, чем клонирование всего списка. К сожалению, поскольку нет стандартного интерфейса «сделать неизменяемый снимок» [обратите внимание, что ICloneable
не считается, так как клон изменяемого списка может быть изменяемым, в то время как можно сделать неизменный снимок, заключив изменяемый клон в оболочку только для чтения, которая будет работать только для объектов, которые можно клонировать, и даже для типов, которые не могут быть клонированы. Я все еще поддерживаю функцию «изменяемого снимка».]