Здравствуйте, 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: Как правильно: публичный метод возвращает список...
Здравствуйте, 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: Как правильно: публичный метод возвращает список...
Здравствуйте, 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>
Здравствуйте, Doc, Вы писали:
Doc>Странный подход. IMHO стоит возвращать самый точный интерфейс (или в некоторых случаях тип).
Это лишает возможности в будущем изменить в библиотеке возвращаемый тип без ломания клиентского кода.
Чем более специфичный тип возвращает автор библиотеки, тем больше обязательств на себя берёт.
Здравствуйте, 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>. То есть вынести решение о стратегии аллокации наружу. Это не так декларативно, и сложнее в использовании, зато гибче. Если пользователю не нравится такой вызов и пофиг на производительность, он сможет и сам легко обернуть во что-нибудь, возвращающее массив.
Здравствуйте, Shmj, Вы писали:
S>? DO NOT use ArrayList or List<T> in public APIs.
Потому что это конкретная реализация, а в 99% случаев достаточно более абстрактных контрактов. Впрочем, если это диктуется требованиями перфоманса, от этого правила можно отступить.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[4]: Как правильно: публичный метод возвращает список...
Здравствуйте, gandjustas, Вы писали:
Doc>>Странный подход. IMHO стоит возвращать самый точный интерфейс (или в некоторых случаях тип). А вот принимать самый обобщенный. G>Выглядит как выставление наружу деталей реализации. G>var конечно все стерпит, но бинарной совместимости уже не будет.
Здравствуйте, 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> не гарантирует ничего, кроме того, что клиент не сможет изменить коллекцию через этот интерфейс. Остальное — лишь допущения.
Как правильно: публичный метод возвращает список...
Вы делаете библиотеку и один из методов вашей либы возвращает список файлов, для примера. Какой тип вы вернете?
Можно IEnumerable<T>, если этот список формируется динамически (yield return или прямая реализация шаблона). Но если он занимает место в памяти целиком — то смысла в IEnumerable нет.
К примеру, Directory.GetFiles() возвращает string[]. Насколько это правильно? Ведь потом кто-то может модифицировать и написать files[0] = "???". Нарушается сама суть — этот список не подлежит изменению.
Емнип, сами MS в своих Guidelines рекомендовали типа ReadOnlyCollection<T> в общих случаях.
Кто серьезно относится к этому вопросу и что делаете?
Здравствуйте, 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]: Как правильно: публичный метод возвращает список...
Здравствуйте, gandjustas, Вы писали:
S>>Вы делаете библиотеку и один из методов вашей либы возвращает список файлов, для примера. Какой тип вы вернете? G>IEnumerable<T>
Странный подход. IMHO стоит возвращать самый точный интерфейс (или в некоторых случаях тип). А вот принимать самый обобщенный.
Re[3]: Как правильно: публичный метод возвращает список...
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, gandjustas, Вы писали:
S>>>Вы делаете библиотеку и один из методов вашей либы возвращает список файлов, для примера. Какой тип вы вернете? G>>IEnumerable<T>
Doc>Странный подход. IMHO стоит возвращать самый точный интерфейс (или в некоторых случаях тип). А вот принимать самый обобщенный.
Выглядит как выставление наружу деталей реализации. var конечно все стерпит, но бинарной совместимости уже не будет.
Здравствуйте, Qbit86, Вы писали:
Q>Это лишает возможности в будущем изменить в библиотеке возвращаемый тип без ломания клиентского кода.
Для того, чтобы изменить возвращаемый тип (а не типы которые используются внутри кода) нужны веские причины. Так что breaking change тут будет даже к лучшему.
Плюс подчеркну, что я за возврат абстракции (интерфейса), а не класса.
Re[4]: Как правильно: публичный метод возвращает список...
Здравствуйте, gandjustas, Вы писали:
Doc>>Странный подход. IMHO стоит возвращать самый точный интерфейс (или в некоторых случаях тип). А вот принимать самый обобщенный. G>Выглядит как выставление наружу деталей реализации.
Подчеркну что я говорю об интерфейса. Поэтому раскрытия реализации тут не больше чем с IEnumerable<T>.
Но это дает более точный контрак клиенту. Например IReadOnlyList<T> говорт что данные уже созданы, размер известен, список нельзя изменить, и можно выбирать по индексу.
Опять же, в каких-то случах например IEnumerable<T> / IAsyncEnumerable<T> будет оптимальным выбором. Весь вопрос в описываемом контракте.
G>var конечно все стерпит, но бинарной совместимости уже не будет.
Я общем случае я против var За исключением кода где тип виден сразу (типа var x = new MyClass(); )
Re[5]: Как правильно: публичный метод возвращает список...
Здравствуйте, Doc, Вы писали:
Doc>Подчеркну что я говорю об интерфейса. Поэтому раскрытия реализации тут не больше чем с IEnumerable<T>.
Doc>Но это дает более точный контрак клиенту. Например IReadOnlyList<T> говорт что Doc>данные уже созданы
не факт. Создан лишь экземпляр, реализующий интерфейс. Doc>размер известен
известен, но не точно. Doc>список нельзя изменить
Интерфейс не позволяет, но изменить коллекцию все-же можно. И код, возвращающий IReadOnlyList<T>, может это сделать позже. Doc>и можно выбирать по индексу.
Все эти гарантии может дать лишь конкретный класс в типе результата. Например, ImmutableList<T>
Re[5]: Как правильно: публичный метод возвращает список...
Здравствуйте, 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]: Как правильно: публичный метод возвращает список...
Здравствуйте, karbofos42, Вы писали:
K>Тут же придётся переделывать контракт и менять клиентов, либо внутри метода возвращать не уже заполненный HashSet, а создавать новый List на его основе и уже его возвращать, т.е. лишняя работа.
Т.е. изначально выбрали не правильную абстракцию. Все дальнейшие проблемы как раз от этого.
Re[6]: Как правильно: публичный метод возвращает список...
Здравствуйте, samius, Вы писали:
Doc>>размер известен S>известен, но не точно.
А вот тут пожалуйста подробнее, где в контректе IReadOnlyList<T> говориться что Count возвращает не точное число элементов.
Нет, понятно что накодить можно что угодно и свести поведение IReadOnlyList<T> к IEnumerable<T> но это лишь будет пример кривой реализации данного интерфейса.
Re[8]: Как правильно: публичный метод возвращает список...
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, karbofos42, Вы писали:
K>>Тут же придётся переделывать контракт и менять клиентов, либо внутри метода возвращать не уже заполненный HashSet, а создавать новый List на его основе и уже его возвращать, т.е. лишняя работа.
Doc>Т.е. изначально выбрали не правильную абстракцию. Все дальнейшие проблемы как раз от этого.
Так вроде бы было, что "стоит возвращать самый точный интерфейс", а теперь оказывается, что есть неправильные абстракции
Re[8]: Как правильно: публичный метод возвращает список...
Здравствуйте, karbofos42, Вы писали:
K>Так вроде бы было, что "стоит возвращать самый точный интерфейс", а теперь оказывается, что есть неправильные абстракции
Я про изнечальное решение "создаётся локальный List<T>, в него заполняются все данные и он же возвращается." Или же я не так понял ваш пример.
Re[9]: Как правильно: публичный метод возвращает список...
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, karbofos42, Вы писали:
K>>Так вроде бы было, что "стоит возвращать самый точный интерфейс", а теперь оказывается, что есть неправильные абстракции
Doc>Я про изнечальное решение "создаётся локальный List<T>, в него заполняются все данные и он же возвращается." Или же я не так понял ваш пример.
Допустим, у нас есть объект со словарём. Есть метод типа:
либо без Linq создаётся лист и в него данные записываются по какому-то принципу.
Потом решили оптимизировать и завели ещё множество для хранения активных элементов, что теперь перебор не нужен и есть готовый HashSet со всеми нужными элементами.
Можно будет переделать на:
public IReadOnlyCollection<Key> GetActiveItemsKeys()
{
return _activeItemsKeys;
}
но тут конечно нельзя сказать, что поведение на 100% осталось прежним и нигде ничего не отвалится.
Контракт остался как был, но есть нюанс, что тоже не есть хорошо и нужно учитывать.