Comments 29
Давно на C# на писал... И тут вздрогнул...
С одной стороны, pattern matching - это шикарно... С другой
int[] ratings = { 5, 4, 3 };
if (ratings is [5, .., >= 3])
{
Console.WriteLine("Great feedback!");
}
Вот не хотел бы я встретиться с чем-то таким в коде...
Я считаю, что любой синтаксический сахар должен использоваться только тогда, когда он действительно упрощает понимание логики, и pattern matching часто позволяет упростить сложные логические выражения. А вот за код, приведенный в комментарии, надо бить по рукам на code review.
А в чем проблема? Вполне читаемо. Использование индексов добавит бойлерплейта, а использование Linq может негативно сказаться на производительности
Писать на C#, массив с ссылочными типами, размещать его в куче и рассуждать о производительности...
По мне уж если до такого дошло - можно и во все тяжкие пуститься
Массив - ссылочный (reference) тип. Элементы массива - int, они value тип. Если быть точным, то readonly struct. Массив объявлен как int[], и боксить элементы свои разумеется не будет. Это не object[] и не ArrayList. Беспокоиться о производительности int array граничит с абсурдом. А вот производительность у linq и правда может быть проблемной. Почему "может быть" - потому что начиная с седьмой версии его существенно оптимизируют и уже в девятке переписали почти весь. И бенчмарки показывают, что не зря.
Если кому-то необходимо (в чем я сомневаюсь) сделать массив еще быстрее, чем он есть, можно попробовать разные трюки. Использовать спаны, играться с содержимым System.Buffers и System.Runtime.InteropServices, творить зубодробительный хардкор с указателями через unsafe, остроумно распоряжаться памятью через fixed и арендовать у сборщика мусора массивы из пула. И много чего еще. Это очень увлекательно и захватывающе. Но скорее всего бесполезно. Если заглянуть в реализацию Array, то можно обнаружить, что там внутри это все уже используется, и даже больше. Реализацию правда в кучу сложновато собрать, но для начала: https://github.com/dotnet/dotnet/blob/main/src/runtime/src/libraries/System.Private.CoreLib/src/System/Array.cs
Больше того, даже тот самый линк под капотом оптимизирован через такое же кунг-фу.
На реальных задачах получить небольшой профит из спанов конечно можно, просто используя их, чтобы сократить лишние аллокации в своем коде. Остальное лучше оставить тем, кто понимает что делает, а по моему опыту это редкие ниндзя. И возвращаясь ближе к контексту: в выше приведенном коде нет проблем с производительностью, там все оптимально.
"Массив - ссылочный (reference) тип. Элементы массива - int, они value тип. Если быть точным, то readonly struct. Массив объявлен как int[],"
вы вообще о чём? Сам массив хранит не элементы, а ссылки на них, это всё, чтобы просто сложить или отсортировать элементы нужно в два раза больше действий, тупик, а добавить туда GC, который постоянно проверяет каждую ссылку? Чем вообще cpu заниматься должен, проверять не накосячил ли программист?
"На реальных задачах получить небольшой профит из спанов конечно можно, просто используя их, чтобы сократить лишние аллокации в своем коде. Остальное лучше оставить тем, кто понимает что делает, а по моему опыту это редкие ниндзя."
Я понимаю что делаю, и конкретно этот пример очевидно в C/C++/Rust разместится в стеке, я могу гарантировать реалтайм даже, понимаете ли вы что если вы пишите на C#, то производительность, точные тайминги и размер в памяти это последнее о чем стоит переживать, там этого добиться почти невероятно
вы вообще о чём? Сам массив хранит не элементы, а ссылки на них
Если это массив value типов, никакие ссылки он не хранит.
Более того, массив при желании можно выделить на стеке, тогда ссылок не будет ни одной:
Span<int> numbers = stackalloc int[] { 1, 2, 3, 4 };
понимаете ли вы что если вы пишите на C#, то производительность, точные тайминги и размер в памяти это последнее о чем стоит переживать, там этого добиться почти невероятно
Хорошо, что вы не успели сказать об этом разработчикам Ryujinx. Они не знали, что это почти невероятно, и написали state-of-the-art эмулятор Nintendo Switch.
Microsoft тоже не знали и написали Garnet.
Прям перловкой смердит от C# , и с каждой версией все больше и больше )))
Выглядит изящно
Дело привычки. Я вот не привык и мне кажется что выглядит уродливо. Откуда там and { Age: > 18 }
и почему именно так - непонятно. Вместо &&
стало and
, вместо person.Age
мы почему то пишем { Age }
, а условие сравнения с > 18
стало : > 18
После некоторого количества лет работы с языком программирования уже не хочется каждый релиз заново учиться читать)
if (color == MyEnum.Orange || color == MyEnum.Yellow || color == MyEnum.Blue)
if (color is MyEnum.Orange or MyEnum.Yellow or MyEnum.Blue)
Можно представить так: всё, что после is, превращается в некую сущность, которой на вход дадут то, что слева от is, и она выдаст true или false. А все эти or, and и not нужны для описания логики работы этой сущности.
Поскольку то, что слева от is, как бы «вынесено за скобки», мы его не упоминаем, поэтому операторы типа > висят неприкаянные.
if (human.age > 5 && human.age < 16)
if (human.age is > 5 and < 16)
А дальше в этом мини‑языке проверок добавляется возможность докапываться до членов того, что слева от is. С помощью фигурных скобок. А двоеточие разделяет член и шаблон, с которым идёт сопоставление.
if (human is {age: > 5 and < 16 })
Ну и получается, что если надо сделать что‑то типа
if ((human.HatColor == MyEnum.Orange || human.HatColor == MyEnum.Yellow) && human.age > 5 && human.age < 16)
то мы можем зашаблонить всё и получить более короткую и лаконичную запись
if (human is {HatColor: MyEnum.Orange or MyEnum.Yellow, age: > 5 and < 16})
Одним словом, мы как бы выносим рассматриваемый объект «за скобки», только это не скобки, а оператор is. И после этого оператора is действуют особые правила описания, где вместо && пишут and, ставят двоеточие и прочие штуки.
Я бы двоеточие на is заменил, если честно. Мне тоже оно не нравится.
О, добавилась запятая! Да, запись стала короче. Но она не стала проще, это целый DSL отдельный =) Напомнило справку по sql, где описание возможного синтаксиса тоже без опыта не разобрать (https://learn.microsoft.com/en-us/sql/t-sql/queries/select-clause-transact-sql?view=sql-server-ver16#syntax)
Я поэтому и говорю, что это дело привычки. Когда такое сам пишешь и регулярно читаешь - наверное привычно и удобно.
Запятая родом из древнего синтаксиса инициализации объектов:
var human = new Human { HatColor = MyEnum.Orange, age = 12 };
Здесь мы тоже юзаем фигурные скобки и запятые, чтобы докапываться до нескольких свойств одного и того же объекта.
Только в инициализации мы задаем значения — приравниваем, поэтому стоят знаки «=», а в том примере — генерим шаблон, поэтому стоят знаки «:». Но в обоих случаях фигурные скобки и докапывания до свойств через запятую.
Мне самому этот синтаксический сахар почти никогда не пригождался, но почему‑то не кажется он мне неудобным. Кривоватый — да, можно чуть лучше сделать. Но вполне юзабельно.
После 10-ти лет работы с Ораклом попервой коробило от "person == null". Ничего, потом привык :) Сейчас в c# можно "person is null", что на мой взгляд гораздо более изящно, чем простое сравнение. Соглашусь, что это чистой воды вкусовщина. Но, в том и прелесть - пишите, как нравится вам!
Утверждение автора о том, что
if (person is not null and { Age: > 18 })
{}
выглядит лучше чем
if (person != null && person.Age > 18)
{}
А именно
Функционально? Да. Удобочитаемо? Не особо. Безопасно? Спорно, особенно когда код становится сложнее.
Выглядит голословно и догматично. В первом if мне всегда надо держать в голове контекст, что age является свойством person. Особенно в случае, если условие усложнится и в нем появится больше свойств этого объекта
if (person is { Age: > 18 })
там is not null лишнее
можно ещё так
if(preson?.Age > 18)
И в примере
if (GetData() is var data && data.Length > 0)
Можно (нужно):
if (GetData() is {Length: > 0} data)
Если рассказываете о фиче, подавайте ее правильно (без лишних синтаксических конструкций).
P.s. может в коротком примере чутка что напутал, с телефона по памяти, но в целом так должно выглядеть.
.
О сколько ждет открытий чудных.. если не следить за релизами языка
Эх.... Ожидал что копнут глубже, и например расскажут не очевидные момент, как тот факт что persona != null можно перегрузить чтобы всегда возвращало true, а вот is null всегда будет проверять именно на существование объекта.
Меня неприятно удивило, что is var спокойно выдает null.
Чем? Если слева Nullable<T>, то и var в него превратиться.
Ну например вот этим:
Program program1 = new Program();
Program program2 = null;
if (program1 is Program p)
{
Console.WriteLine("Program1 is not null");
}
if (program2 is var p2)
{
Console.WriteLine("Program2 is not null? No it is null");
}
Выглядят почти одинаково, но первый нулл не выдаст никогда.
При объявлении локальной переменной можно разрешить компилятору определить тип переменной из выражения инициализации. Для этого используйте
var
ключевое слово вместо имени типа:
При использовании
var
в контексте с поддержкой, допускающего значение NULL, и тип выражения инициализации является ссылочным типом, компилятор всегда вводит ссылочный тип, допускающий значение NULL, даже если тип выражения инициализации не является пустым.
Этого достаточно, что бы is отдал null. Тут более подробно: Expressions - C# language specification | Microsoft Learn
Позиционные образцы
Код не работает, можно же было хотя бы проверить :)
Что бы работало как надо, надо передавать одну переменную Point в switch, тогда оно будет по координатам работать... Такое ощущение пример стащили, а как работает не поняли.
Сопоставление с образцом на C#: объяснение и примеры