Как правильно: публичный метод возвращает список...
От: Shmj Ниоткуда  
Дата: 08.07.21 17:14
Оценка:
Вы делаете библиотеку и один из методов вашей либы возвращает список файлов, для примера. Какой тип вы вернете?

Можно IEnumerable<T>, если этот список формируется динамически (yield return или прямая реализация шаблона). Но если он занимает место в памяти целиком — то смысла в IEnumerable нет.

К примеру, Directory.GetFiles() возвращает string[]. Насколько это правильно? Ведь потом кто-то может модифицировать и написать files[0] = "???". Нарушается сама суть — этот список не подлежит изменению.

Емнип, сами MS в своих Guidelines рекомендовали типа ReadOnlyCollection<T> в общих случаях.

Кто серьезно относится к этому вопросу и что делаете?
Отредактировано 08.07.2021 17:16 Shmj . Предыдущая версия .
Re: Как правильно: публичный метод возвращает список...
От: samius Япония http://sams-tricks.blogspot.com
Дата: 08.07.21 17:30
Оценка: 16 (1) +4
Здравствуйте, Shmj, Вы писали:

S>Вы делаете библиотеку и один из методов вашей либы возвращает список файлов, для примера. Какой тип вы вернете?


S>К примеру, Directory.GetFiles() возвращает string[]. Насколько это правильно? Ведь потом кто-то может модифицировать и написать files[0] = "???". Нарушается сама суть — этот список не подлежит изменению.


Вполне правильно. Метод Directory.GetFiles() гарантирует то, что он вернет коллекцию адекватную (на какой-то момент времени в прошлом). Есть гарантии, что получатель ответа (вызвавший Directory.GetFiles()) получит эту коллекцию в должном виде. Но Directory.GetFiles() не обязан гарантировать что кто-то, получив эту коллекцию, не сможет сломать её.

S>Емнип, сами MS в своих Guidelines рекомендовали типа ReadOnlyCollection<T> в общих случаях.

что это меняет в случае Directory.GetFiles()? Кто-то, получив такой ответ от GetFiles(), сможет подменить его другим ответом и сломать себе ногу.

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

S>Кто серьезно относится к этому вопросу и что делаете?

Все вполне серьезно. Надо просто провести разделитель, когда надо свое состояние спасать от изменений, а когда возвращается Value, пусть даже изменяемое...
Re: Как правильно: публичный метод возвращает список...
От: Ночной Смотрящий Россия  
Дата: 08.07.21 18:49
Оценка: +2
Здравствуйте, Shmj, Вы писали:

S>К примеру, Directory.GetFiles() возвращает string[]. Насколько это правильно?


Правильно.

S> Ведь потом кто-то может модифицировать и написать files[0] = "???".


Может.

S>Нарушается сама суть — этот список не подлежит изменению.


С чего ты взял, что это сама суть?

S>Кто серьезно относится к этому вопросу и что делаете?


Не решаю проблемы, которых не существует на практике.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re: Как правильно: публичный метод возвращает список...
От: Kolesiki  
Дата: 08.07.21 19:11
Оценка: -2 :)
Здравствуйте, Shmj, Вы писали:

S>Вы делаете библиотеку и один из методов вашей либы возвращает список файлов, для примера. Какой тип вы вернете?


Рекомендуют возвращать IEnumerable<T>, потому что это самый "узкий" интерфейс, который можно подсовывать в кучу алгоритмов и аргументов.

Но по-моему, везде надо подходить с головой и смотреть на саму задачу. Те же файлы: с одной стороны, хочется предостеречься от случая "чел запросил миллион файлов" и только постепенно возвращать ему результат для обработки, не нагружая память. Но с другой стороны, за время работы энумератора половина файлов могла исчезнуть! И тогда энумератор возвратит не то количество файлов, которое было в момент запроса! А значит надо ВСЮ коллекцию вычитывать сразу в память и возвращать как List.

Вообще, учитывая, что расходы на List<> "почти равны" другим коллекциям, я бы смело возвращал везде List<T> и не выёживался преждевременными оптимизациями.
Почему? Да потому что List<T> — самый "полноценный" список! Захотел — добавил элементы, захотел — отсортировал, отфильтровал и т.п.

S>К примеру, Directory.GetFiles() возвращает string[]. Насколько это правильно?


Вообще неправильно!

S>Ведь потом кто-то может модифицировать и написать files[0] = "???". Нарушается сама суть — этот список не подлежит изменению.


Вот именно, что это только ты решил (ну и M$-макаки впридачу), что файлы менять нельзя! С какого перепоя??! Ты что, знаешь ВСЕ ЗАДАЧИ программиста? Может, он хочет перед списком файлов добавить псевдофайл "..", означающий переход в родительский каталог! (как в Windows Commander) Но ты испортил ему задачу и ему придётся брать твою неуклюжую коллекцию и создавать отдельный, модифицируемый список, перелопачивая туда элементы.

S>Кто серьезно относится к этому вопросу и что делаете?


А в ИТ всё — серьёзно! И всё — несерьёзно. Как "виртуальный роман".

Я бы провозгласил главный принцип создания API таким: "НЕ ДУМАЙ ЗА ЮЗЕРА БОЛЬШЕ, ЧЕМ ТРЕБУЕТ ТВОЯ ЗАДАЧА". Не ограничивай его без надобности, думай наперёд, что варианты использования функции могут быть ОЧЕНЬ непредсказуемыми! Дай юзеру всю свободу, а уж он сам решит У СЕБЯ В ПРОГРАММЕ, каких случаев надо бояться и какие коллекции брать.

Смешной до идиотизма пример: MS придумала, что в VS если ты подцепился к списку intellisense, то ты можешь только ДОПОЛНЯТЬ этот список!
В результате, если даже в данном контексте применимы всего два пункта, ты всё равно будешь видеть и перебирать ВЕСЬ мусор, который MS любезно насрала в "подсказки".
Вот так "умники", которые умничают больше юзера, обгадились на элементарной задаче.


Ну как, всё серьёзно?
Re: EnumerateFiles()
От: Qbit86 Кипр
Дата: 08.07.21 20:38
Оценка: 6 (1)
Здравствуйте, Shmj, Вы писали:

S>Можно IEnumerable<T>, если этот список формируется динамически (yield return или прямая реализация шаблона).

S>К примеру, Directory.GetFiles() возвращает string[].

А Directory.EnumerateFiles() возвращает IEnumerable<string>.

S>...возвращает string[]. Насколько это правильно?


Это прибивает гвоздями решение аллоцировать массив менеджером памяти. Плохо для пользователей, которые пишут чувствительный к memory-трафику код.
В лучшем случае автор библиотеки это сделает через GC.AllocateUninitializedArray{T}(), но кто про него знает? А пользователь библиотеки может предоставить более эффективную для своего сценария стратегию получения массивов; скажем, через ArrayPool{T} или stackalloc.

S>Емнип, сами MS в своих Guidelines рекомендовали типа ReadOnlyCollection<T> в общих случаях.


Многие современные стандартные API и вовсе запрашивают выходной Span<string>. Правда, это требует API для предварительной оценки количества элементов, что вряд ли оправдано конкретно для списка файлов.

S>Но если он занимает место в памяти целиком — то смысла в IEnumerable нет...

S>Кто серьезно относится к этому вопросу и что делаете?

Я серьёзно отношусь к этому вопросу, и вместо GetFiles() вероятнее всего сделал бы PopulateFiles(), передавая туда ICollection<string>, или лучше TCollection where TCollection : ICollection<string>, или вовсе конкретный класс коллекции, а может и Span<string>. То есть вынести решение о стратегии аллокации наружу. Это не так декларативно, и сложнее в использовании, зато гибче. Если пользователю не нравится такой вызов и пофиг на производительность, он сможет и сам легко обернуть во что-нибудь, возвращающее массив.
Глаза у меня добрые, но рубашка — смирительная!
Отредактировано 08.07.2021 20:50 Qbit86 . Предыдущая версия . Еще …
Отредактировано 08.07.2021 20:48 Qbit86 . Предыдущая версия .
Отредактировано 08.07.2021 20:47 Qbit86 . Предыдущая версия .
Отредактировано 08.07.2021 20:45 Qbit86 . Предыдущая версия .
Отредактировано 08.07.2021 20:43 Qbit86 . Предыдущая версия .
Отредактировано 08.07.2021 20:41 Qbit86 . Предыдущая версия .
Re: Как правильно: публичный метод возвращает список...
От: varenikAA  
Дата: 09.07.21 01:11
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Вы делаете библиотеку и один из методов вашей либы возвращает список файлов, для примера. Какой тип вы вернете?


S>Можно IEnumerable<T>, если этот список формируется динамически (yield return или прямая реализация шаблона). Но если он занимает место в памяти целиком — то смысла в IEnumerable нет.


S>К примеру, Directory.GetFiles() возвращает string[]. Насколько это правильно? Ведь потом кто-то может модифицировать и написать files[0] = "???". Нарушается сама суть — этот список не подлежит изменению.


S>Емнип, сами MS в своих Guidelines рекомендовали типа ReadOnlyCollection<T> в общих случаях.


S>Кто серьезно относится к этому вопросу и что делаете?


Правильный ответ: Type First
Если вы хотите отдать список файлов, то тип возвращаемый должен быть не стринг, а FileInfo допустим.
Массив или перечисление это решать вам. В общем случае массив, дальше уже можно как перечисление.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[2]: Как правильно: публичный метод возвращает список...
От: Shmj Ниоткуда  
Дата: 09.07.21 04:50
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

S>>К примеру, Directory.GetFiles() возвращает string[]. Насколько это правильно?

НС>Правильно.

Ок. А почему рекомендуют не использовать List:

❌ DO NOT use ArrayList or List<T> in public APIs.
Re[3]: Как правильно: публичный метод возвращает список...
От: Ночной Смотрящий Россия  
Дата: 09.07.21 08:26
Оценка: +1
Здравствуйте, Shmj, Вы писали:

S>? DO NOT use ArrayList or List<T> in public APIs.


Потому что это конкретная реализация, а в 99% случаев достаточно более абстрактных контрактов. Впрочем, если это диктуется требованиями перфоманса, от этого правила можно отступить.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re: Как правильно: публичный метод возвращает список...
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 09.07.21 08:45
Оценка: +2
Здравствуйте, Shmj, Вы писали:

S>Вы делаете библиотеку и один из методов вашей либы возвращает список файлов, для примера. Какой тип вы вернете?

IEnumerable<T>

S>Можно IEnumerable<T>, если этот список формируется динамически (yield return или прямая реализация шаблона). Но если он занимает место в памяти целиком — то смысла в IEnumerable нет.


S>Емнип, сами MS в своих Guidelines рекомендовали типа ReadOnlyCollection<T> в общих случаях.

S>Кто серьезно относится к этому вопросу и что делаете?
IEnumerable<T>
IReadOnlyCollection<T>
IReadOnlyList<T>
IReadOnlyDictionary<Key,Value>
Re[2]: Как правильно: публичный метод возвращает список...
От: Doc Россия http://andrey.moveax.ru
Дата: 14.07.21 14:39
Оценка:
Здравствуйте, gandjustas, Вы писали:

S>>Вы делаете библиотеку и один из методов вашей либы возвращает список файлов, для примера. Какой тип вы вернете?

G>IEnumerable<T>

Странный подход. IMHO стоит возвращать самый точный интерфейс (или в некоторых случаях тип). А вот принимать самый обобщенный.
Re[3]: Возврат точного типа
От: Qbit86 Кипр
Дата: 14.07.21 14:56
Оценка: +2
Здравствуйте, Doc, Вы писали:

Doc>Странный подход. IMHO стоит возвращать самый точный интерфейс (или в некоторых случаях тип).


Это лишает возможности в будущем изменить в библиотеке возвращаемый тип без ломания клиентского кода.
Чем более специфичный тип возвращает автор библиотеки, тем больше обязательств на себя берёт.
Глаза у меня добрые, но рубашка — смирительная!
Re[3]: Как правильно: публичный метод возвращает список...
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 14.07.21 15:55
Оценка:
Здравствуйте, Doc, Вы писали:

Doc>Здравствуйте, gandjustas, Вы писали:


S>>>Вы делаете библиотеку и один из методов вашей либы возвращает список файлов, для примера. Какой тип вы вернете?

G>>IEnumerable<T>

Doc>Странный подход. IMHO стоит возвращать самый точный интерфейс (или в некоторых случаях тип). А вот принимать самый обобщенный.

Выглядит как выставление наружу деталей реализации.
var конечно все стерпит, но бинарной совместимости уже не будет.
Re[4]: Как правильно: публичный метод возвращает список...
От: Sharov Россия  
Дата: 14.07.21 17:29
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

Doc>>Странный подход. IMHO стоит возвращать самый точный интерфейс (или в некоторых случаях тип). А вот принимать самый обобщенный.

G>Выглядит как выставление наружу деталей реализации.
G>var конечно все стерпит, но бинарной совместимости уже не будет.

https://en.wikipedia.org/wiki/Robustness_principle
Кодом людям нужно помогать!
Re[4]: Возврат точного типа
От: Doc Россия http://andrey.moveax.ru
Дата: 16.07.21 07:02
Оценка:
Здравствуйте, Qbit86, Вы писали:

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


Для того, чтобы изменить возвращаемый тип (а не типы которые используются внутри кода) нужны веские причины. Так что breaking change тут будет даже к лучшему.
Плюс подчеркну, что я за возврат абстракции (интерфейса), а не класса.
Re[4]: Как правильно: публичный метод возвращает список...
От: Doc Россия http://andrey.moveax.ru
Дата: 16.07.21 07:10
Оценка:
Здравствуйте, gandjustas, Вы писали:

Doc>>Странный подход. IMHO стоит возвращать самый точный интерфейс (или в некоторых случаях тип). А вот принимать самый обобщенный.

G>Выглядит как выставление наружу деталей реализации.

Подчеркну что я говорю об интерфейса. Поэтому раскрытия реализации тут не больше чем с IEnumerable<T>.

Но это дает более точный контрак клиенту. Например IReadOnlyList<T> говорт что данные уже созданы, размер известен, список нельзя изменить, и можно выбирать по индексу.
Опять же, в каких-то случах например IEnumerable<T> / IAsyncEnumerable<T> будет оптимальным выбором. Весь вопрос в описываемом контракте.

G>var конечно все стерпит, но бинарной совместимости уже не будет.


Я общем случае я против var За исключением кода где тип виден сразу (типа var x = new MyClass(); )
Re[5]: Как правильно: публичный метод возвращает список...
От: samius Япония http://sams-tricks.blogspot.com
Дата: 16.07.21 07:45
Оценка:
Здравствуйте, Doc, Вы писали:

Doc>Подчеркну что я говорю об интерфейса. Поэтому раскрытия реализации тут не больше чем с IEnumerable<T>.


Doc>Но это дает более точный контрак клиенту. Например IReadOnlyList<T> говорт что

Doc>данные уже созданы
не факт. Создан лишь экземпляр, реализующий интерфейс.
Doc>размер известен
известен, но не точно.
Doc>список нельзя изменить
Интерфейс не позволяет, но изменить коллекцию все-же можно. И код, возвращающий IReadOnlyList<T>, может это сделать позже.
Doc>и можно выбирать по индексу.

Все эти гарантии может дать лишь конкретный класс в типе результата. Например, ImmutableList<T>
Re[5]: Как правильно: публичный метод возвращает список...
От: karbofos42 Россия  
Дата: 16.07.21 07:57
Оценка:
Здравствуйте, Doc, Вы писали:

Doc>Здравствуйте, gandjustas, Вы писали:


Doc>>>Странный подход. IMHO стоит возвращать самый точный интерфейс (или в некоторых случаях тип). А вот принимать самый обобщенный.

G>>Выглядит как выставление наружу деталей реализации.

Doc>Подчеркну что я говорю об интерфейса. Поэтому раскрытия реализации тут не больше чем с IEnumerable<T>.


Doc>Но это дает более точный контрак клиенту. Например IReadOnlyList<T> говорт что данные уже созданы, размер известен, список нельзя изменить, и можно выбирать по индексу.

Doc>Опять же, в каких-то случах например IEnumerable<T> / IAsyncEnumerable<T> будет оптимальным выбором. Весь вопрос в описываемом контракте.

Ну, изначально кто-то сделал реализацию метода в виде: создаётся локальный List<T>, в него заполняются все данные и он же возвращается.
В итоге сделали возврат IList<T> или IReadOnlyList<T>.
Потом решили оптимизировать метод и оказалось, что этот List приходится постоянно перебирать, чтобы в результат не попали дубликаты, заменили на HashSet какой-нибудь.
Если бы возвращали IEnumerable<T>, IReadOnlyCollection<T>,... то вернули бы так же уже готовую коллекцию и не парились.
Тут же придётся переделывать контракт и менять клиентов, либо внутри метода возвращать не уже заполненный HashSet, а создавать новый List на его основе и уже его возвращать, т.е. лишняя работа.
Re[6]: Как правильно: публичный метод возвращает список...
От: Doc Россия http://andrey.moveax.ru
Дата: 19.07.21 04:12
Оценка:
Здравствуйте, karbofos42, Вы писали:

K>Тут же придётся переделывать контракт и менять клиентов, либо внутри метода возвращать не уже заполненный HashSet, а создавать новый List на его основе и уже его возвращать, т.е. лишняя работа.


Т.е. изначально выбрали не правильную абстракцию. Все дальнейшие проблемы как раз от этого.
Re[6]: Как правильно: публичный метод возвращает список...
От: Doc Россия http://andrey.moveax.ru
Дата: 19.07.21 04:16
Оценка:
Здравствуйте, samius, Вы писали:

Doc>>размер известен

S>известен, но не точно.

А вот тут пожалуйста подробнее, где в контректе IReadOnlyList<T> говориться что Count возвращает не точное число элементов.

Нет, понятно что накодить можно что угодно и свести поведение IReadOnlyList<T> к IEnumerable<T> но это лишь будет пример кривой реализации данного интерфейса.
Re[7]: Как правильно: публичный метод возвращает список...
От: samius Япония http://sams-tricks.blogspot.com
Дата: 19.07.21 04:42
Оценка: +1
Здравствуйте, Doc, Вы писали:

Doc>Здравствуйте, samius, Вы писали:


Doc>>>размер известен

S>>известен, но не точно.

Doc>А вот тут пожалуйста подробнее, где в контректе IReadOnlyList<T> говориться что Count возвращает не точное число элементов.

Я о том, что число элементов может быть изменено после того, как клиент получил инстанс IReadOnlyList<T>.


Doc>Нет, понятно что накодить можно что угодно и свести поведение IReadOnlyList<T> к IEnumerable<T> но это лишь будет пример кривой реализации данного интерфейса.

System.Collections.Generic.List<T> — пример не самой кривой реализации IReadOnlyList<T>, которая позволяет изменять число элементов после того, как клиент получил экземпляр.

На самом деле контракт IReadOnlyList<T> не гарантирует ничего, кроме того, что клиент не сможет изменить коллекцию через этот интерфейс. Остальное — лишь допущения.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.