CSHARP_GEPARD Telegram 118
Танцы вокруг Enumerable.ToArray #скорость #память

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

При получении IEnumerable из какого-либо метода, мы часто делаем ToList или ToArray. Например, чтобы получить возможность пробежаться по перечислению более чем один раз (см. multiple enumerations в случае IEnumerable). Или, например, мы не хотим аллоцировать Enumerator при пробегании по IReadonlyCollection. Короче говоря, по каким-то перформансным соображениям, нам интерфейсы не подходят.

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

Далее действия могут быть различными и зависеть от знаний глубин .NET'a.

Например, некоторые коллеги верят, что если они вызовут метод ToArray, то произойдёт магия: мол, dotnet знает настоящий тип коллекции, которая возвращается из метода, а значит просто его и вернёт. Увы, это не так. Если мы посмотрим код, то можно заметить, что при вызове Enumerable.ToArray создаётся новый массив с копией данных исходного, который и возвращается потребителю.

Другие коллеги будут более упорными в желании добраться до исходной коллекции, и создадут метод AsArray. Он, я уверен, есть во многих проектах. Этот метод прост, он проверяет тип, и, если это действительно массив, просто возвращает его. Если же это другой тип коллекции, то будет использоваться стандартный Enumerable.ToArray.

Скорость сильно выше, никаких дополнительных аллокаций сделано не будет (см. бенчмарк), а значит будет сделан вывод, что это идеальное решение для ситуаций работы с приходящим IEnumerable. Код, думаю, будет примерно таким:


public static T[] AsArray<T>(this IEnumerable<T> collection)
{
return collection as T[] ?? collection.ToArray();
}


Казалось бы win-win. Но нет. И это вторая логическая ошибка.

Если мы ожидаем массив, исходный метод делает массив и мы придумали костыль, чтоб всё-таки получить массив... не лучше ли просто возвращать массив? Зачем эти танцы с бубном?

Если это наш код и нам ну очень нужен конкретный тип коллекции - давайте просто его использовать. Да, если до этого был IEnumerable, а теперь стал массив - это ломающее изменение и оно не подойдёт для библиотеки с тыщами потребителей... но уж внутри нашего приложения медиаторный хэндлер мы поправить в состоянии.

Ещё одним неприятным моментом будет изменение исходной коллекции. Например, мы получили перечисление, скастили его к массиву и изменили... А оно, например, служит неким кэшем. Как результат, возможно очень странное поведение в других местах кода, которые полагаются на неизменяемость исходной коллекции.

Короче говоря, мне кажется, что иногда не нужно изобретать велосипед. В данном случае, я в этом почти уверен.

P.S.: Александр, спасибо!
P.P.S.: Для серьёзных ребят Денис сделал собственный перечислитель для разных случаев. Кажется, что это очень хорошее решение, которое ликвидирует минусы подобного подхода.
👍15👎107



tgoop.com/csharp_gepard/118
Create:
Last Update:

Танцы вокруг Enumerable.ToArray #скорость #память

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

При получении IEnumerable из какого-либо метода, мы часто делаем ToList или ToArray. Например, чтобы получить возможность пробежаться по перечислению более чем один раз (см. multiple enumerations в случае IEnumerable). Или, например, мы не хотим аллоцировать Enumerator при пробегании по IReadonlyCollection. Короче говоря, по каким-то перформансным соображениям, нам интерфейсы не подходят.

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

Далее действия могут быть различными и зависеть от знаний глубин .NET'a.

Например, некоторые коллеги верят, что если они вызовут метод ToArray, то произойдёт магия: мол, dotnet знает настоящий тип коллекции, которая возвращается из метода, а значит просто его и вернёт. Увы, это не так. Если мы посмотрим код, то можно заметить, что при вызове Enumerable.ToArray создаётся новый массив с копией данных исходного, который и возвращается потребителю.

Другие коллеги будут более упорными в желании добраться до исходной коллекции, и создадут метод AsArray. Он, я уверен, есть во многих проектах. Этот метод прост, он проверяет тип, и, если это действительно массив, просто возвращает его. Если же это другой тип коллекции, то будет использоваться стандартный Enumerable.ToArray.

Скорость сильно выше, никаких дополнительных аллокаций сделано не будет (см. бенчмарк), а значит будет сделан вывод, что это идеальное решение для ситуаций работы с приходящим IEnumerable. Код, думаю, будет примерно таким:


public static T[] AsArray<T>(this IEnumerable<T> collection)
{
return collection as T[] ?? collection.ToArray();
}


Казалось бы win-win. Но нет. И это вторая логическая ошибка.

Если мы ожидаем массив, исходный метод делает массив и мы придумали костыль, чтоб всё-таки получить массив... не лучше ли просто возвращать массив? Зачем эти танцы с бубном?

Если это наш код и нам ну очень нужен конкретный тип коллекции - давайте просто его использовать. Да, если до этого был IEnumerable, а теперь стал массив - это ломающее изменение и оно не подойдёт для библиотеки с тыщами потребителей... но уж внутри нашего приложения медиаторный хэндлер мы поправить в состоянии.

Ещё одним неприятным моментом будет изменение исходной коллекции. Например, мы получили перечисление, скастили его к массиву и изменили... А оно, например, служит неким кэшем. Как результат, возможно очень странное поведение в других местах кода, которые полагаются на неизменяемость исходной коллекции.

Короче говоря, мне кажется, что иногда не нужно изобретать велосипед. В данном случае, я в этом почти уверен.

P.S.: Александр, спасибо!
P.P.S.: Для серьёзных ребят Денис сделал собственный перечислитель для разных случаев. Кажется, что это очень хорошее решение, которое ликвидирует минусы подобного подхода.

BY C# Heppard




Share with your friend now:
tgoop.com/csharp_gepard/118

View MORE
Open in Telegram


Telegram News

Date: |

On June 7, Perekopsky met with Brazilian President Jair Bolsonaro, an avid user of the platform. According to the firm's VP, the main subject of the meeting was "freedom of expression." best-secure-messaging-apps-shutterstock-1892950018.jpg The optimal dimension of the avatar on Telegram is 512px by 512px, and it’s recommended to use PNG format to deliver an unpixelated avatar. The public channel had more than 109,000 subscribers, Judge Hui said. Ng had the power to remove or amend the messages in the channel, but he “allowed them to exist.” Add the logo from your device. Adjust the visible area of your image. Congratulations! Now your Telegram channel has a face Click “Save”.!
from us


Telegram C# Heppard
FROM American