C# - У чего приоритет больше - у операторов или паттернов?
Например, такое выражение
if (enumValue == MyEnum.One || enumValue == MyEnum.Two || enumValue == MyEnum.Three)
можно записать так - гораздо короче и понятнее
if (enumValue is MyEnum.One or MyEnum.Two or MyEnum.Three)
А что если в if проверяется ещё что-то? Например
if (obj != null || enumValue == MyEnum.One || enumValue == MyEnum.Two || enumValue == MyEnum.Three)
Тогда можно записать
if (obj != null || enumValue is MyEnum.One or MyEnum.Two or MyEnum.Three)
Но тогда непонятно, что вперёд выполняется. Где в спецификации языка или в МСДН написано, что операторы имеют бОльший приоритет, чем паттерны?
Или ваше предложение, как написать понятнее, но чтобы не портянку как в третьей строчке.
Нашёл только это пока
https://learn.microsoft.com/en-us/dotnet/csharp/language-r...
Но тут про приоритет лишь паттернов, но не паттернов и операторов.
ну вроде ж очевидно, что сначала выполняется is..or, а потом ||
Или твой вопрос был не в этом?
Для твоего случая можно написать что-нибудь типа этого
int number = 5; bool res = number.In(1,2,3,4); public static bool In<T>(this T item, params T[] items) { if (items == null) throw new ArgumentNullException("items"); return items.Contains(item); }
Ну, для меня было не очевидно. Вообще, все "очевидности" должны быть явно прописаны - вот я и спрашивал, может кто знает, где точно указано, что операторы вперёд паттернов.
Да для таких вещей не надо ничего расширяющего писать - всё есть. Если руками каждое отдельное число забивать, то проще массив через скобочки. Если сгенерить последовательность подряд идущих чисел, то проще Enumerable.Range или что-то такое. И потом IEnumerable.Contains. У меня вариант, когда надо проверить на равенство двум-трём значениям. Обычно это для перечислений или чего подобного с небольшим количеством возможных значений. Просто неохото писать портянку || или && с повторением проверяемого значения.
Вобщем, уже понял, что проще делать типа такого
A is B or C or D
или для понятливости
A is (B or C
or D)
Да для таких вещей не надо ничего расширяющего писать - всё есть. Если руками каждое отдельное число забивать, то проще массив через скобочки. Если сгенерить последовательность подряд идущих чисел, то проще Enumerable.Range или что-то такое. И потом IEnumerable.Contains.
По мне так это гораздо более громоздко чем в моём примере.
ну вроде ж очевидно, что сначала выполняется is..or, а потом ||
Почему очевидно?
if (obj != null || enumValue is MyEnum.One or MyEnum.Two or MyEnum.Three)
ЕМНИП вычисление выражений идет слева направо, так что сначала будет вычислено "obj != null" и если это выражение true, то все остальное в принципе не должно вычисляться.
Просто неохото писать портянку || или && с повторением проверяемого значения.
Вобщем, уже понял, что проще делать типа такого
A is B or C or D
или для понятливости
A is (B or C or D)
Для понятливости надо избегать смешивания разных видов записей.
И чем проще выражение внутри if'а, тем лучше.
В твоем случае лучше было бы записать так:
bool allowedValue = enumValue is MyEnum.One or MyEnum.Two or MyEnum.Three;
if (obj != null || allowedValue)
Просто неохото писать портянку || или && с повторением проверяемого значения.
Вобщем, уже понял, что проще делать типа такого
A is B or C or D
или для понятливости
A is (B or C or D)
Для понятливости надо избегать смешивания разных видов записей.
И чем проще выражение внутри if'а, тем лучше.
В твоем случае лучше было бы записать так:
bool allowedValue = enumValue is MyEnum.One or MyEnum.Two or MyEnum.Three;
if (obj != null || allowedValue)
Я бы избежал, но в одном паттерне, насколько я понял, нельзя использовать, скажем так, разные данные. Я попробовал сделать заменить это
if (obj != null || (enumValue is MyEnum.One or MyEnum.Two or MyEnum.Three))
на это
if (obj != null or (enumValue is MyEnum.One or MyEnum.Two or MyEnum.Three))
или даже так
if (obj is not null or
(enumValue is (MyEnum.One or MyEnum.Two or MyEnum.Three)))
- так нельзя. Ругается что-то типа, что obj и enumValue разные данные и их нельзя в одном паттерне использовать. Хотя по логике, оба паттерна "возвращают" bool - потому почему бы не использовать? Основная идея была - не повторять для каждого сравнения enumValue.
Паттерны вообще что-нибудь "возвращают"?
Немного не так. Есть пара переменных, значения которых надо проверить. Одну - на null, вторую - на равенство некоторым значениям из перечисления. По сути, это либо портянка if-else, либо портянка из логических операторов с кучей скобок. Паттерновые конструкции позволяют сделать эти портянки поменьше чисто за счёт менее многословного синтаксиса. Только и всего. Ну а дополнительные переменные просто сделают портянку более читаемой, но всё равно оставят портянкой. Можно ещё со свичём поиграться и кортежами - все возможные комбинации загнать в кортежи и сделать их кейсами свича. Но при числе комбинаций больше 3 это уже само по себе та ещё портянка.
Щас прочитал, в чём отличие использования операторов от паттернов. Паттерны нельзя перегружать. Поэтому при их использовании с логикой, аналогичной операторной, тебе гарантируется, что ты не попадёшь на какую-нибудь незамеченную перегрузку оператора, которая сломает тебе код.
любишь "код с сахаром" (syntax shugar)?
почитай еще здесь, может будет интересно.
https://blog.ndepend.com/csharp9-new-keywords-for-pattern-...
но если перфоманс не критичен, то, конечно, следует выбрать более читабельный вариант. или что диктуют "правила кодирования", примятые в данной конторе (если имеются).
Щас попробовал поэкспериментировать со свичом и кортежами - плохо получается. Т.е. типа
switch (a, b, c)
(,,) => true,
(,,) => true,
(,,) => true,
_ => false,
где в скобках возможные сочетания переменных а, b, с. Но проблема возникает, если одна из переменных nullable, как я раньше писал в примерах, и нужно проверить, что она не null. Вот null можно, а литерала "not null" нет. Приходится тогда всю эту "таблицу" кортежей инвертировать и перебирать варианты с возвратом false, а всё остальное - true. Но если вариантов с false в разы больше, то получается совсем большая портянка. А вот паттерны "is, is not, and, or" делают это всё короче - буквально в одну строчку.
Ну там по сути перечисляется то, что у же давно в МСДН описано. Только добавить, как я выше написал, что новые паттерны не только заменяют некоторые операторы, но и предоставляют доступ именно к неперегруженной логике (в отличие от операторов, которые могут быть перегружены).
Ну и мой пример приведён:
A better example is the expressionif(x is 1 or 2 or 3)
. It is more compact thanif((x==1) || (x==2) || (x==3))
and makes sense.
Вобщем, эта штука удобна в том числе, когда надо проверить переменную на соответствие некоторым значениям перечисления.
Если много разных кейсов обработки переменной на разные значения перечисления - то лучше выражение switch -
weekDay switch =>
{
DaysOfWeek.Monday => ...,
DaysOfWeek.Tuesday => ...,
DaysOfWeek.Wednesday => ...,
...
_ =>
};
Если один кейс на меньшинство значений перечисления, то лучше паттерн "is (or...)" -
bool isHoliday = weekDay is (DaysOfWeek.Saturday or DaysOfWeek.Sunday);
Если один кейс на большинство значений перечисления, то лучше паттерн "is not (or...)" -
bool isWorkingDay = weekDay is not (DaysOfWeek.Saturday or DaysOfWeek.Sunday);
Всё это можно и операторами написать, конечно, но будет раза в полтора длинее и больше разных значков, тогда как выражения с паттернами в основном состоят из слов.