C# - pattern matching - many discards
Вопрос про оформление и красивости. И можно ли вообще так написать.
Вобщем, понадобилось проверить объект на кучку разномастных условий. Например, на налл весь объект, на значение одно из свойств, если объект не налл, и вернуть что-то другое, если какие-то ещё условия. И хочу спросить, как удобнее сделать, чтобы не писать простыную из вложенных ифов. Пусть мы получили где-то объект row кастомного типа и хотим сделать подобные проверки. Как вы находите такую запись - удобной, читаемой, или лучше пачку вложенных ифов?
var result = row switch { null => MyEnum.NotAllowed, _ when row.Age > 100 => MyEnum.Died, _ => MyEnum.StillAlive, };
Тут результат быдет перечислением MyEnum.
Насколько я знаю, все выражения в выражении свича проверяются по порядку написания до первого соответствия, так что первая проверка на налл всего объекта - нормально. Меня смутило только возможность сделать несколько дискардов, т.к. полный объект мне не нужен. Ну или может можно как-то без дискардов обойтись.
Я могу так переписать, но мне что-то первый вариант больше нравится - можно добавить проверок других свойств row, и тогда логично, что входной параметр свича это весь объект. Но в любом случае есть пачка дискардов - не смущает ли она?
var result = row.Age switch { _ when row == null => MyEnum.NotAllowed, > 100 => MyEnum.Died, _ => MyEnum.StillAlive, };
Альтернатива в ифах
MyEnum result; if (row != null) { if (row.Age > 100) { result = MyEnum.Died; } else { result = MyEnum.StillAlive; } } else { result = MyEnum.NotAllowed; }
Ситуация с ифами ухудшается, если количество проверок увеличивается. А в сопоставлениях с образцом - просто добавляется одна строчка на проверку.
Древовидная структура ифов легко трансформируется в линейную структуру сопоставлений, просто порядок соблюдать надо - сначала одну ветку ифов последовательно в сопоставлениях пишем, потом другую. Разве что если условий становится больше десятка, как и вложенностей - по 3-4 и больше. Но и тогда на мой взгляд сопоставления лучше - они хотя бы на экран влезут, а дерево ифов явно расползётся куда дальше. Ну и всегда можно отделить группы кейсов в сопоставлениях форматированием - добавить между ними пустую строчку, например. А в ифах уже и так места нет, так ещё их удлинять.
var result = row.Age switch { _ when row == null => MyEnum.NotAllowed, > 100 => MyEnum.Died, _ => MyEnum.StillAlive, };
Не уверен, но тут, по-моему, может NullReferenceException вылететь уже на row.Age, если row налл. Ну можно тогда так написать row?.Age.
var result
Вот 3 варианта получить "result" на чистом сишарповском языке. Результат везде одинаков.
Похоже мы опять изобретаем очередной велосипед.
Этих реализаций дофига, например
https://docs.fluentvalidation.net/en/latest/start.html
https://github.com/Blazored/FluentValidation
https://code-maze.com/complex-model-validation-in-blazor/
и свитч и ифы не подходят, если уж свое сильно хочется.
ну так написал же... Пишешь просто набор правил
RuleFor(x => x.Surname).NotEmpty(); RuleFor(x => x.Forename).NotEmpty().WithMessage("Please specify a first name"); RuleFor(x => x.Discount).NotEqual(0).When(x => x.HasDiscount); RuleFor(x => x.Address).Length(20, 250); RuleFor(x => x.Postcode).Must(BeAValidPostcode).WithMessage("Please specify a valid postcode");
RuleFor(x => x.Name).NotEmpty().WithMessage("name cannot be null");
RuleFor(x => x.Phone).NotEmpty();
RuleFor(x => x.Other).GreaterThan(0).WithMessage("other must be great than 0");;
ну так написал же... Пишешь просто набор правил
Вы чтобы в туалет сходить, сначала строите маленький заводик по производству туалетной бумаги? А если понос? ))
Дискарды юзаю только потому, что в условиях должен быть тип входного для свича параметра.
Можно бы было произвольные условия проверки писать - обошёлся бы без этого костыля. Но произвольно в паттерн матчинг пока не завезли.
Во первых, всё уже написано,
во вторых, был вопрос как нравится
Вы не поняли - проверки могут быть где угодно, а не лишь внутри ваших безнес-правил. И там не обязательно объект с полями, а может быть просто скажем строка. Проверяем её на налл, длину, наличие такого или иного символа или их сочетания... Ну понятно, вы и тут правила свои напишете - в виде расширения класса строк. Любой лапшекод, лишь бы паттерн матчинг не использовать )))
Ситуация с ифами ухудшается, если количество проверок увеличивается. А в сопоставлениях с образцом - просто добавляется одна строчка на проверку.
Перепиши if так, чтобы была одна строчка на проверку.
MyEnum result; if (row == null) result = MyEnum.NotAllowed; else if (row.Age > 100) result = MyEnum.Died; else result = MyEnum.StillAlive;
Тоже можно. Но текста куда больше из-за засилья словесных операторов и присвоения в каждой строчке.
С другой стороны, в моём варианте используются ненужные мне дискарды в качестве костылей. В идеале должно быть [условие] => [возврат].
Вот красным отметил информационный мусор в моём и вашем вариантах
В идеале должно быть [условие] => [возврат].
------
Об этом мы спорили во времена Аксесс 2.
есть плюсы... есть и минусы...
Самый простой вопрос - у тебя два и более различаемых условий независящих от переменной в свитче - что писать будешь?
Чуть более сложный - что с производительностью при большом числе правил?
Самый простой вопрос - у тебя два и более различаемых условий независящих от переменной в свитче - что писать будешь?
Свободно можно использовать. Как я выше написал - сначала нужен костыль-значение с типом переменной, или дискард, а затем "when" и далее любые проверки с любыми переменными из контекста, в котором находится данный свитч. Разве что результат такой проверки должен быть булевым.
Про производительность не знаю. По-моему, раз все эти проверки условий (if-else, switch) взаимозаменяемы, то они генерятся в одинаковый исполняемый код. Их разные записи - лишь для удобства использования в разных случаях. А значит вопрос производительности того или другого высокоуровневого кода не стоит - стоит вопрос производительности итогового исполняемого кода. Ну, думаю, там какую-нибудь хеш-таблицу замутили на все обозначенные программистом комбинации условий, так что при проходе по ветке условий сразу нужная имплементация выполняется.
проверки могут быть где угодно
ну тогда и ничего обсуждать о подобной программе, делайте как нравится и будет счастливы.
Я бы еще посчитал, что нам потребуется для предыдущего кода во всех местах.
вы и тут правила свои напишете
единого решения для всех случаев жизни не существует