Что вы предпочитаете для интерфейсов: T [], IEnumerable <T>, IList <T>или другие? - PullRequest
11 голосов
/ 21 сентября 2009

Хорошо, я надеюсь, что сообщество в целом поможет нам в решении дебатов на рабочем месте, которые продолжаются некоторое время. Это связано с определением интерфейсов, которые принимают или возвращают списки определенного типа. Есть несколько способов сделать это:

public interface Foo
{
    Bar[] Bars { get; }
    IEnumerable<Bar> Bars { get; }
    ICollection<Bar> Bars { get; }
    IList<Bar> Bars { get; }
}

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

public interface Foo
{
    void Do(IEnumerable<Bar> bars);
    Bar[] Bars { get; }
}

Мой аргумент в пользу этого подхода заключается в том, что класс реализации может создать список непосредственно из IEnumerable и просто вернуть его с помощью List.ToArray (). Однако некоторые считают, что IList должен быть возвращен вместо массива. Проблема у меня здесь заключается в том, что теперь вам необходимо снова скопировать его с ReadOnlyCollection перед возвратом. Возможность возврата IEnumerable кажется проблемной для клиентского кода?

Что вы используете / предпочитаете? (особенно в отношении библиотек, которые будут использоваться другими разработчиками вне вашей организации)

Ответы [ 8 ]

18 голосов
/ 21 сентября 2009

Мои предпочтения IEnumerable<T>. Любой другой из предложенных интерфейсов создает видимость, позволяющую потребителю изменять базовую коллекцию. Это почти наверняка не то, что вы хотите сделать, поскольку это позволяет потребителям молча изменять внутреннюю коллекцию.

Еще один хороший ИМХО, это ReadOnlyCollection<T>. Он учитывает все забавные свойства .Count и Indexer и недвусмысленно говорит потребителю: «Вы не можете изменять мои данные».

15 голосов
/ 21 сентября 2009

Я не возвращаю массивы - они действительно являются ужасным типом возврата, который следует использовать при создании API - если вам действительно нужна изменяемая последовательность, используйте интерфейс IList<T> или ICollection<T> или вместо этого возвращайте конкретный Collection<T>.

Также я бы посоветовал вам прочитать Массивы, которые Эрик Липперт считает несколько вредными :

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

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

Позвольте мне начать с того, что когда вы определенно не следует использовать массивы, и тогда воск более философски о будущее современного программирования и Роль массива в грядущем мире.

8 голосов
/ 21 сентября 2009

Для коллекций свойств, которые проиндексированы (и индексы имеют необходимое семантическое значение), вы должны использовать ReadOnlyCollection<T> (только для чтения) или IList<T> (чтение / запись). Это самый гибкий и выразительный. Для неиндексированных коллекций используйте IEnumerable<T> (только для чтения) или ICollection<T> (для чтения / записи).

Параметры метода должны использовать IEnumerable<T>, если они 1) не нуждаются в добавлении / удалении элементов в коллекцию (ICollection<T>) или 2) не требуют индексов для необходимых семантических целей (IList<T>). Если метод может извлечь выгоду из доступности индексации (такой как подпрограмма сортировки), он всегда может использовать as IList<T> или .ToList(), когда это не удается в реализации.

1 голос
/ 21 сентября 2009

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

В этих терминах это означает, что мне нравится принимать самый слабый интерфейс, возможный в качестве аргументов метода, потому что это делает мой код полезным из большего количества мест. В данном случае это IEnumerable<T>. Есть массив? Вы можете вызвать мой метод. Есть список? Вы можете вызвать мой метод. Есть блок итератора? Вы поняли.

Это также означает, что мне нравятся мои методы для возврата самого сильного интерфейса, который удобен, так что код, основанный на методе, может легко сделать больше. В этом случае это будет IList<T>. Обратите внимание, что это не значит, что я создам список только для того, чтобы я мог его вернуть. Это просто означает, что если у меня уже есть некоторый, который реализует IList<T>, я могу также использовать его.

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

1 голос
/ 21 сентября 2009

Я бы предпочел IEnumerable, поскольку он является самым высокоуровневым из интерфейсов, предоставляя конечному пользователю возможность повторной трансляции по своему усмотрению. Даже при том, что это может предоставить пользователю минимальные функциональные возможности для начала (в основном, только перечисление), этого все равно будет достаточно для покрытия практически любых потребностей, особенно с помощью методов расширения, ToArray (), ToList () и т. Д.

0 голосов
/ 22 сентября 2009

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

Когда вы хотите предотвратить изменения, вы уже оборачиваете их / копируете, поэтому я не вижу, что не так с IEnumerable или ReadOnlyCollection. Я хотел бы пойти с последним ... что-то, что мне не нравится в IEnumerable, это то, что он ленивый по своей природе, но когда вы используете только предварительно загруженные данные, чтобы обернуть его, вызов кода, который работает с ним, имеет тенденцию предполагать загруженные данные или лишние «лишние» строки :( ... которые могут привести к неприятным результатам во время изменений.

0 голосов
/ 22 сентября 2009

Поскольку методы расширения Linq были добавлены в IEnumerable , я обнаружил, что использование других интерфейсов значительно сократилось; вероятно, около 80%. Раньше я использовал список неукоснительно, так как в нем были методы, которые принимали делегатов для отложенной оценки, такие как Find, FindAll, ForEach и тому подобное. Поскольку это доступно через расширения System.Linq, я заменил все эти ссылки на IEnumerable ссылки.

0 голосов
/ 21 сентября 2009

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

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

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

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