Комментарии 52
У вас какая-то странная IDE. У меня подсвечивает (vscode)
Rider тоже такое не пропустил
Hidden text
Обмажутся своими блокнотами, а потом в ноги стреляют!
Вы абсолютно правы, сейчас Rider подсвечивает эту ошибку. Это был не блокнот, а Rider, хотя я не могу сказать точную версию. Либо эта ошибка исправилась с обновлениями, либо были проблемы с системой подсказок локально.
Я думаю, что не IDE должна показывать такие ошибки, а компилятор должен запрещать компилировать подобный код.
VS2022 подствечивает там потенциальную ошибку.
Может раньше не было правда.
Спасибо, что протестировали это в своем IDE. В данной ситуации использовался Rider. Подробнее написал тут: https://habr.com/ru/articles/764586/#comment_26015534
Очень круто что сейчас ошибки подсвечиваются.
Вроде все логично: конструкция с фигурными скобками разворачивается в серию последовательных вызовов .Add()
на коллекции, и нововведением это не назовешь - кажется, еще с версии C# 5.0 доступно. Иногда эта возможность действительно удобна, например когда поле является get-only:
class Foo {
public List<int> A { get; set; } = new List<int>();
public List<int> B { get; } = new List<int>();
}
var foo = new Foo
{
A = new List<int> { 1, 2 }, // работает
// B = new List<int> { 3, 4 } // не сработает - ошибка компиляции
B = { 3, 4 } // сработает
};
А вот то, что ошибка показывается на первой строке выражения, а не на той где действительно указана вызывающая ошибку операция - это досадная недоработка.
Ну не знаю, в моем понимании ExList = { 1, 2 } это присваивание объекту некоего составного литерала, а никакие не Add. Так что создатели C# здесь сделали фигню. Добавление там new не изменяет семантики всего выражения - это по прежнему присваивание объекту другого объекта.
Если пытаться прочитать код на языке X, опираясь на интуицию и знание какого-то другого языка Y, то абсолютно любая фича языка X может показаться странной. Наверное, программисту на PHP покажется, что foo.bar
- это конкатенация двух строк, а не обращение к методу, и так далее. Такой подход едва ли конструктивен.
В сишарпе добавление new
меняет-таки семантику, потому что new { ... }
- это выражение, и new () { ... }
- это выражение, а вот просто { ... }
- нет. Его нельзя сохранить в переменную или передать в функцию. Соответственно, когда вы пишете справа от знака равно что-то, что не является выражением, это не может быть обычным присваиванием и должно быть чем-то еще - в данном случае, инициализацией коллекции через последовательные вызовы метода Add
.
Если у вас есть идеи, как можно было бы описать инициализацию вложенной коллекции лучше - поделитесь, пожалуйста.
ExList ~= { 1, 2 }
Ну или как там у вас конкатенация обозначается.
ExList += { 1, 2 } // add semantic
ExList = { 1, 2 } // assign semantic
Мне кажется как то получше чем то что есть. + это должно работать как то так для присваивания
ExList = { 1, 2 } =>
if ExList.IsNull () { ExList = new() { 1, 2 } }
else { ExList.Clear(); ExList = { 1, 2 } }
И как то так для добавления
if ExList.IsNull() { ExList = new() {1, 2 } }
else { ExList.Add(1); ExList(2); }
Я не очень представляю в каком случае будет иметь смысл НЕ инициализировать объект заданными значениями если он нулл, а кидать исключение как сейчас делает такой синтаксис, ну и явный += как то заметней чем разное поведение в зависимости от отсутвия наличия new ()
Вообще как раз мнение людей не пишущих на этом языке о синтаксисе часто менее предвзяты чем мнение тех кто пишет на нем каждый день, так что зря товарища выше минусят.
Если сделать так, то снова получается неконсистентность — при инициализации оператор += есть, а отдельно его нету. А если сделать его отдельно, то снова беда: один и тот же оператор вызывает то operator +
, то метод .Add()
в зависимости от того что справа. А сделать всё через один метод нельзя — потому что {1, 2}
не литерал объекта, а просто синтаксис для инициализации коллекции.
если сделать его отдельно, то снова беда: один и тот же оператор вызывает то
operator +
, то метод.Add()
в зависимости от того что справа.
А в чем собственно беда? Сахар он на то и сахар что бы вот такие неконсистености скыравать за простым синтаксисом, ну или я не понял о чем вы.
снова беда: один и тот же оператор вызывает то operator +, то метод .Add() в зависимости от того что справа
возможно тут написано что-то на джававском, но в моем понимании одно должно вызывать другое… неужели в шарпах не так?
Спасибо за полезный пример применения, он добавляет ясности. Да, действительно, это нельзя назвать нововведением, но в тот момент людей знающих этот синтаксис не оказалось рядом и для меня это было чем то новым.
Конструкция new() без указания типа вообще была введена как аналог var, но для R-value
То есть можно написать либо:
var l = new List<int>(){1, 2, 3};
либо
List<int> l = new(){1,2,3};
Но причём тут NRE? А всё дело в том, что
{0, 1}
заменяет конструкцию.Add()
И вот эти два блока кода оказываются идентичными:
А это так вообще первый раз слышу. И IDE почему-то тоже не в курсе
Да, действительно, это не работает с обычным Add
. Эта конструкция используется только при инициализации объекта. Выше подсказали хороший пример использования: https://habr.com/ru/articles/764586/#comment_26015170.
C# 12 collection expressions решает эту проблему.
Вот именно из-за всего этого я ушёл с C#. Писал на нём с 2003 года. Помню как вышел .NET 1.1. Помню как было круто с дженериками в .NET 2.0.
Я прилежно учился и следил за всеми новыми выпусками .NET. Я помню тот день, когда Скотт Хансельман показал гитхаб для ASP.NET.
Я думал о том, как будет круто когда сам шарп превратился в по-настоящему открытый язык. Но нет. Это его просто убило в моих глазах.
Я вообще не понимаю, зачем надо было смешивать строго типизированный язык с Яваскриптом и чисто функциональными языками. Что не так с MyClass c = new MyClass()? Учитывая с тем, что всё на ватокомплите и после набора первых трёх быкв вижуал студия [и любая другая ИДЕ] могла всё автозаполнить. Неочевидно, но наличие var эту фитчу отключило. В любом случае, что ты будешь делать с сэкономленными фемтосекундами?
После того, как самый простой switch-case превратился в тьюнинг-полного монстра я ушёл в мир голанга.
Все эти свистоперделки и рюшечки превратили отличный язык в перл. Сейчас код на шарпах выглядит одинаково до и после шифрования файлов исходника.
думаю вы ушли на голанг потому, что за него больше платят
С таким подходом вы не заработаете денег. Сейчас я вообще пишу на ноде, потому что этого требует бизнес. Я - профессиональный программист. Это значит, что я не промываю мозги своему начальнику рассказами о том, как хорош или плох какой-то ЯП. Я просто беру проблему, которую нужно решить для бизнеса и решаю её без рассказов о том, на каком языке она пишется. Я просто решаю проблемы. За это платят намного больше, чем за рассказы о хороших языках.
Примечательно, что безобидное высказывание вызвало у кого-то негатив :)
Работодатели всё-таки ищут не просто [профессиональных] разработчиков, а тех у кого есть опыт с определенным стеком. Нельзя написать в резюме "я решаю задачи бизнеса" и стать нарасхват.
Перекосы в разных стеках есть - не в разы конечно, но всё же C#/.NET никогда не славился большими ЗП.
С таким подходом вы не заработаете денег.
Здесь я бы не стеки новые учил, а масштабировал свои усилия для большего кол-ва результата -- шел в бизнес и т.п.
Поддерживаю - постоянное добавление новых возможностей потихоньку превращает его в C++ в котором можно сделать одно и тоже отстрелить себе ногу 1001 способом. Как мне кажется, на версии 8 вполне можно было остановиться - выразительности более чем достаточно, а дальнейшее добавление новых фич будет лишь во вред
зачем надо было смешивать строго типизированный язык с Яваскриптом и чисто функциональными языками
Тут можно дать очень обширный ответ, но если в кратце - то потому, что так удобнее. Например, работа с коллекциями через LINQ гораздо лаконичнее, удобнее и нагляднее, чем если писать все то же самое на циклах. Кроме того, некоторые особенности функционального стиля (чистые функции, иммутабельные переменные, композиция функций) позволяют легче распараллелить и отлаживать код.
Что не так с MyClass c = new MyClass()?
Да всё в порядке. Если визуальный шум вам не мешает, то пишите на здоровье. В стайлгайдах Microsoft принято в большинстве случаев указывать тип, если только он не очевиден по выражению справа, как у вас. Но многим людям это кажется избыточным, поэтому они используют `var`.
самый простой switch-case превратился в тьюнинг-полного монстра
Опять же, обычный switch-case остался как есть. Вам никто не запрещает писать так, как вы писали во времена 15 лет назад во времена .NET 2.0. Лично мне новый switch expression тоже не нравится, я считаю его избыточным и плохо вписывающимся в язык, поэтому я им практически никогда не пользуюсь. Портит ли его наличие язык в целом? Едва ли, хотя разработчики инструментария (Resharper, Rider, статических анализаторов и т.д.), которые вынуждены это поддерживать, наверняка со мной не согласятся.
Вывод - язык развивается в ту сторону, в которую его толкают пользователи. Выходит так, что большинство людей хочет писать лаконичный, но в то же время понятный код. Эта золотая середина у каждого своя, поэтому то, что дизайнеры языка C# не попали лично в вашу - не особо удивительно. В то же время, вы нашли язык, который вам больше по душе, что очень здорово.
Проблема не в том, что я могу это всё пропускать мимо ушей. Проблема в том, что я работаю с другими людьми, и код, который ко мне приходит выглядит как регекс, а не как код. Другие-то люди пишут на этом.
С технической точки зрения все решаемо: поставьте минимальный langversion в проекте, настройте codestyle на свое усмотрение с помощью Stylecop, Resharper, Roslyn Analyzers - на здоровье. Останется только найти людей, которые согласятся в таких условиях работать
Дело в том, что если вы должны писать тону кода и развивать огромный проект, то switch expression позволяет писать то же самое, но быстрее - это здорово экономит время и превращает сотню if в элегантную конструкцию, и становится проще анализировать код, который занимает много места. В то же время бывают спорные решения, но благо развитие языка открыто и можно предлагать свои или обсуждать предложенные другими функции на github.
Мне не нравится, что switch expression служит той же цели, но отличается от switch statement практически во всем: ключевое слово switch
пишется после выражения а не до, вместо default
используется _
, вместо case
- стрелка =>
, и самое важное - не проваливается на следующий вариант при отсутствии break
.
Вместо этого можно было все унифицировать:
Писать
switch
всегда перед проверяемым выражением. Разделять на switch expression и switch statement по тому, располагается ли оно внутри выражения или нет.Разрешить
X => Y
в switch statement как синонимcase X: Y; break;
, по аналогии с expression body в свойствах и функцияхРазрешить
_
как синонимdefault
Не делать
case > 1
, а использовать более общийcase var x when x > 1
Самое досадное, что в Java это сделали как надо, а в C# не смогли.
Кроме того, некоторые особенности функционального стиля (чистые функции, иммутабельные переменные, композиция функций) позволяют легче распараллелить и отлаживать код.
Это вы про C# или вообще?
Если про C# то покажите мне способ получиь гарантию от компилятора, что делегат, который мне могут передать в мою функцию, не может не быть чистой функцией. Я такого способа, например, не знаю. А в отсутствии этой гарантии от компилятора функциональное программирование превращается в «функциональщину», которая ничего такого прекрасного не позволит.
Вообще я говорил в целом о подходе. Чистоту функций обычно связывают с ФП, потому что понятие "оттуда родом", но оно вполне неплохо ложится и на императивный код. То, что компилятор не может автоматом это гарантировать, возможно несколько ограничивает его удобство, но не перечеркивает его целиком. LINQ, например, используется повсеместно, и я не слышал особых жалоб на то, что возможность передать туда лямбду с побочными эффектами рушит всю идею. Наоборот, если это локально и вы понимаете что делаете, то может быть даже удобно - например, вставить логирование посреди длинной цепочки вызовов.
Какие-то смузи-стайл аргументы, уж извините. Я ушёл из C# потому что, всё не так, я думал оно вот так, а оно вон оно как, не так и не эдак. Зачем так надо было-то? То то, то это, не то ни это, монстр какой-то.
Сейчас код на шарпах у нас решает множество задач. При чём производительных, под высокими нагрузками с весьма скромными требованиями. Наблюдаемость у проектов на C# феноменальная. С чем только и как не интегрировались, работает как часы.
Когда в Go внезапно что-нибудь добавят, и там найдётся свой процент ненавистников "свистоперделок", которые уйдут на что-нибудь очередное хайповое. Ох уж эти свистоперделки, никакого спасу от них нет :)))))
Ничего оно не отключило. Если внимат
Имхо, во всех примерах проблема не с сахаром, а с кодом
Есть теория, что "хороший интерфейс должно быть сложно использовать неправильно". То есть, даже если вы используете сахар, неправильный код должен выглядеть инородно, неправильно, вызывать подозрения. Я не шарпист, для меня то, как оно было написано - выглядело целиком нормально. Автор статьи - шарпист. И для него это тоже выглядело нормально. Если неправильное использование синтаксиса выглядит настолько органично, то, на мой вкус, проблема все же в синтаксисе, а не в использующем коде.
Код абсолютно верный и рабочий когда коллекция существует.
var result = new ExampleClass
{
ExString = "Тут не должно быть взрыва",
ExList = { 0, 1 },
ExString2 = "И тут",
ExInt = 24
};
Смотрите, автор указал такой код.
Если я напишу без сахара, типа как
var result = new ExampleClass();
result.ExList.Add(0);
result.ExList.Add(1);
То он так же развалится, тут не в сахаре дело.
Плюс эта ошибка всегда подсвечивается IDE, о чём автору сообщили в самых первых комментариях (не знаю, в чём он код пишет), а если ещё и настроить всё правильно, то можно даже ошибку компиляции получить.
Признаться, я не очень понимаю, к чему вы ведете. В коде используется синтаксический сахар. Из-за использования сахара получается то, о чем я (да и вы, в общем-то) сказал - выглядящий совершенно нормально код содержит в себе проблему. Если убрать сахар - мы увидим код, который совершенно очевидно содержит в себе ошибку. То есть, проблема не в исходном синтаксисе языка, а в синтаксическом сахаре.
Хотя, зная о подобных выкрутасах, я бы, наверное, допустимый на проекте сахар как-то ограничивал и тогда это можно считать проблемой кода, да.
Само собой, комментарии об IDE совершенно логичны. Еще можно настроить синтаксический анализатор.
Из-за использования сахара получается то, о чем я (да и вы, в общем-то) сказал - выглядящий совершенно нормально код содержит в себе проблему
Но ведь и без сахара код выглядит совершенно нормально
Признаться, я не очень понимаю, к чему вы ведете
К тому, что, очевидно, нужно знать, что делает та или иная конструкция в языке, будь то сахар или нет.
А вся история автора выглядит очень странно, начиная от отсутствия подсказок IDE и заканчивая
Всё началось с того момента, когда в нашем проекте начали "выстреливать" ошибки NRE.
Очевидно, что код из статьи начал бы сыпаться при первом же запуске, а не неожиданно начал выстреливать где то. Либо автор чего то недоговаривает, либо намеренно упростил код и там вокруг ещё куча не очень хорошего кода. Такой код должны были обнаружить сначала при разработке, потом на ревью, а потом и при тестировании (я всё таки предполагаю, что 'в нашем проекте начали "выстреливать" ошибки NRE' - это где то на проде или на тестовом контуре, а не при локальной разработке).
Собственно, я и веду к тому, что автор на очень неудачном примере пытается показать, что сахар плохой. А плохой тут не сахар, а код, вот и всё.
По поводу ограничения сахара - мне кажется тут всё таки решается всё культурой разработки в первую очередь.
А разве включение nullable начиная с 8 версии не спасает?
Синтаксический сахар упрощает написание кода, но никто не мешает писать без него, если есть такая проблема.
Единственное разбирать чужой код из сахара проблема.
Допустим мне бы в голову не могло прийти что такое вообще возможно на c#: (a, b) = (b, a). Это обмен значений между переменными, кто не в курсе.
Забавно, я думал, что только в питоне идут споры про приемлемость применения list comprehension и подобных сокращений/улучшений/ухудшений кода, а оказывается нет)
Синтаксический диабет. Извините.
Недостаточное покрытие тестами.
Как синтаксический сахар может сыграть с вами злую шутку