Здравствуйте, Shmj, Вы писали:
S>https://m.habr.com/ru/company/piter/blog/501520/
S>Пишут что сглупили, теперь хотят все исправить. Ваше мнение?
Я считаю, что самая большая ошибка в борьбе с NRE — это борьба с NRE.
Если заменить null на какое-нибудь значение, которое будет тихо пропускаться при обработке, то станет ещё хуже: ошибка никуда не денется, но вместо явного падения будет выдаваться неправильный результат где-то совсем в другом месте и отследить будет на порядок сложнее.
Ведь чтобы избежать ошибок компиляции 90% разработчиков просто понавтыкают при объявлении переменной инициализацию в пустую строку.
Да, часть таких ошибок отловит компилятор, но весьма малую часть. А отлов остальных значительно усложнится, и получится ещё более дорогая проблема.
Похожая ситуация была в джаве с явным объявлением exceptions. Насколько я смотрел в джавовский код, никто эти не пользуется, так как в мало-мальски сложной програме отследить это всё равно невозможно.
Точно также и с null — мало кто будет пользоваться новыми возможностями и основная проблема не пофиксится, но добавится новая
Re[2]: C# 8 и null-допустимость - ошибка на миллиард
V>Ведь чтобы избежать ошибок компиляции 90% разработчиков просто понавтыкают при объявлении переменной инициализацию в пустую строку.
И это не завернут на code-review.
V>Точно также и с null — мало кто будет пользоваться новыми возможностями и основная проблема не пофиксится, но добавится новая
Х-з. Я просто включил в проекте эту нуллабельность, вылетело несколько тыщ варнингов, за пару недель работы над другими фичами число припало до нескольких сотен. Через месяц поставил warning as error, и полечили. Х-з помогло оно от реальных ошибок или нет — покрытие тестами было хорошее, но ощущение что догадываешься что все объекты у тебя инициализированы надёжно и их можно не проверять, вполне приятное.
Re[2]: C# 8 и null-допустимость - ошибка на миллиард
Здравствуйте, vmpire, Вы писали:
V>Здравствуйте, Shmj, Вы писали:
S>>https://m.habr.com/ru/company/piter/blog/501520/
S>>Пишут что сглупили, теперь хотят все исправить. Ваше мнение? V>Я считаю, что самая большая ошибка в борьбе с NRE — это борьба с NRE. V>Если заменить null на какое-нибудь значение, которое будет тихо пропускаться при обработке, то станет ещё хуже: ошибка никуда не денется, но вместо явного падения будет выдаваться неправильный результат где-то совсем в другом месте и отследить будет на порядок сложнее. V>Ведь чтобы избежать ошибок компиляции 90% разработчиков просто понавтыкают при объявлении переменной инициализацию в пустую строку.
V>Да, часть таких ошибок отловит компилятор, но весьма малую часть. А отлов остальных значительно усложнится, и получится ещё более дорогая проблема. V>Похожая ситуация была в джаве с явным объявлением exceptions. Насколько я смотрел в джавовский код, никто эти не пользуется, так как в мало-мальски сложной програме отследить это всё равно невозможно. V>Точно также и с null — мало кто будет пользоваться новыми возможностями и основная проблема не пофиксится, но добавится новая
А в плюсах вполне себе используют указатели и ссылки и не слышал, чтобы кто-то говорил, что ссылки не нужны и из-за них появляются трудноуловимые ошибки.
Re[3]: C# 8 и null-допустимость - ошибка на миллиард
Здравствуйте, karbofos42, Вы писали:
K>А в плюсах вполне себе используют указатели и ссылки и не слышал, чтобы кто-то говорил, что ссылки не нужны и из-за них появляются трудноуловимые ошибки.
Не путай шарпников и сишников.
Re[3]: C# 8 и null-допустимость - ошибка на миллиард
Здравствуйте, karbofos42, Вы писали:
K>А в плюсах вполне себе используют указатели и ссылки и не слышал, чтобы кто-то говорил, что ссылки не нужны и из-за них появляются трудноуловимые ошибки.
Ну так я как раз и пишу, что не нужно решать несуществующие проблемы
Re[2]: C# 8 и null-допустимость - ошибка на миллиард
Здравствуйте, vmpire, Вы писали:
V>Здравствуйте, Shmj, Вы писали:
S>>https://m.habr.com/ru/company/piter/blog/501520/
S>>Пишут что сглупили, теперь хотят все исправить. Ваше мнение? V>Я считаю, что самая большая ошибка в борьбе с NRE — это борьба с NRE. V>Если заменить null на какое-нибудь значение, которое будет тихо пропускаться при обработке, то станет ещё хуже: ошибка никуда не денется, но вместо явного падения будет выдаваться неправильный результат где-то совсем в другом месте и отследить будет на порядок сложнее.
Ну, фича вроде бы не про замену null на какое-нибудь значение. А про чёткий контроль контрактов: т.н. "явное падение" очень часто происходит тогда, когда по колл стеку уже ничего не разберёшь.
Ну вот обратились мы к полю ._xx, а оно — null. Почему оно null — да х.з. Надо разматывать назад не только стек, а всю предысторию — откуда, что, куда.
Это дорого и трудно. Куда лучше, если об этом нам скажет компилятор и заранее. Тогда у нас будет возможность обложить все места, откуда в наш ._хх может приехать null, явными проверками с throw ArgumentNullException. V>Ведь чтобы избежать ошибок компиляции 90% разработчиков просто понавтыкают при объявлении переменной инициализацию в пустую строку.
Ну, не всякую переменную можно инициализировать пустой строкой.
V>Похожая ситуация была в джаве с явным объявлением exceptions. Насколько я смотрел в джавовский код, никто эти не пользуется, так как в мало-мальски сложной програме отследить это всё равно невозможно. V>Точно также и с null — мало кто будет пользоваться новыми возможностями и основная проблема не пофиксится, но добавится новая
Посмотрим. Пока что у первых ласточек отзывы восторженные.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: C# 8 и null-допустимость - ошибка на миллиард
Здравствуйте, karbofos42, Вы писали: K>А в плюсах вполне себе используют указатели и ссылки и не слышал, чтобы кто-то говорил, что ссылки не нужны и из-за них появляются трудноуловимые ошибки.
Ссылки и в C# есть и прекрасно работают. Указатели тоже существуют, но это уже унсейв.
Тут проблема в том, что есть методы в которые нужно передавать не null.
И как раз указав, что параметр должен быть не null то идет контроль на уровне компилятора.
Частично решается с помощью атрибутов [NotNull], для подсказки всяким решарперам и прочим. Но решили, что типы NotNull это правильнее.
Но это касается еще и возвращаемых значений.
Я в общем то согласен.
и солнце б утром не вставало, когда бы не было меня
S>>>Пишут что сглупили, теперь хотят все исправить. Ваше мнение? V>>Я считаю, что самая большая ошибка в борьбе с NRE — это борьба с NRE. V>>Если заменить null на какое-нибудь значение, которое будет тихо пропускаться при обработке, то станет ещё хуже: ошибка никуда не денется, но вместо явного падения будет выдаваться неправильный результат где-то совсем в другом месте и отследить будет на порядок сложнее. S>Ну, фича вроде бы не про замену null на какое-нибудь значение. А про чёткий контроль контрактов:
Это понятно. Но возьмите какой-нибудь реальный существующий код крупного/среднего проекта. Пришло указание использовать новую фичу. Включили контроль. Попадала куча ошибок.
Как будет фиксить типовой средний разработчик? Правильно, понатыкает везде инициализацию в пустую строку, чтобы компилировалось.
И вместо NRE будет затруднённый поиск ошшибок.
S>Ну вот обратились мы к полю ._xx, а оно — null. Почему оно null — да х.з. Надо разматывать назад не только стек, а всю предысторию — откуда, что, куда.
Проверка аргументов (которую Вы упомянули) отлично решает эту проблему, а заодно и с другими типами и другими видами ошибок в аргументах.
Жаль, CodeContracts забросили, как раз бы подошло.
S>Это дорого и трудно. Куда лучше, если об этом нам скажет компилятор и заранее. Тогда у нас будет возможность обложить все места, откуда в наш ._хх может приехать null, явными проверками с throw ArgumentNullException.
Атрибут [NotNull] уже есть, можно было просто ввести его в язык.
V>>Ведь чтобы избежать ошибок компиляции 90% разработчиков просто понавтыкают при объявлении переменной инициализацию в пустую строку. S>Ну, не всякую переменную можно инициализировать пустой строкой.
Ну не пустую строку, а, например, пустой конструктор, который чаще всего есть.
Re[4]: C# 8 и null-допустимость - ошибка на миллиард
Здравствуйте, vmpire, Вы писали:
V>Это понятно. Но возьмите какой-нибудь реальный существующий код крупного/среднего проекта. Пришло указание использовать новую фичу. Включили контроль. Попадала куча ошибок. V>Как будет фиксить типовой средний разработчик? Правильно, понатыкает везде инициализацию в пустую строку, чтобы компилировалось.
Это если речь о строке. А если нет? V>И вместо NRE будет затруднённый поиск ошшибок.
Ну, если этим парням сейчас NRE не мешает, то вряд ли помешает пустая строка. Ну, свалится у них код не с NRE, а с FileName cannot be empty, или с тем, что string.Length у них вернул нуль туда, куда нуль не принимают.
V>Проверка аргументов (которую Вы упомянули) отлично решает эту проблему, а заодно и с другими типами и другими видами ошибок в аргументах.
Решает, но только там, где она есть. V>Жаль, CodeContracts забросили, как раз бы подошло.
Совершенно верно. Вот они NotNull contract вшили в компилятор.
V>Атрибут [NotNull] уже есть, можно было просто ввести его в язык.
Я так понимаю, их разочаровал уровень использования [NotNull]. Поэтому они решили пойти несколько более насильственным путём: дать способ массово зафорсить этот [NotNull].
V>Ну не пустую строку, а, например, пустой конструктор, который чаще всего есть.
Ну х.з. По мне так его как раз чаще всего нету; а если есть — то он производит вполне валидный объект, который отлично сработает.
Просто где-то был code path, когда этот мембер не инициализировался, а теперь такого code path не будет.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: C# 8 и null-допустимость - ошибка на миллиард
Здравствуйте, Sinclair, Вы писали:
S>Ну вот обратились мы к полю ._xx, а оно — null. Почему оно null — да х.з. Надо разматывать назад не только стек, а всю предысторию — откуда, что, куда.
Какое-то плохое объяснение... Чем это принципиально отличается от: Ну вот обратились мы к полю ._xx, а оно — "". Почему оно "" — да х.з. Где вместо "" может быть [], 0 и т.д.
По-моему основная проблема, что null насильно добавляет ещё одно значение в область значений всех типов которое в подавляющем большинстве случаев не нужно, но заставляет явно об этом помнить каждый раз, усложняет код часто бессмысленными проверками и/или грозит падением. Притом, если скажем int/string/etc можно обернуть в класс и заставить его быть только в определённом диапазоне, то с null такой трюк не пройдёт, выразить кодом, что null тут нет или есть — не получится.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: C# 8 и null-допустимость - ошибка на миллиард
Здравствуйте, Serginio1, Вы писали:
S>Здравствуйте, karbofos42, Вы писали: K>>А в плюсах вполне себе используют указатели и ссылки и не слышал, чтобы кто-то говорил, что ссылки не нужны и из-за них появляются трудноуловимые ошибки. S>Ссылки и в C# есть и прекрасно работают. Указатели тоже существуют, но это уже унсейв. S>Тут проблема в том, что есть методы в которые нужно передавать не null. S>И как раз указав, что параметр должен быть не null то идет контроль на уровне компилятора. S>Частично решается с помощью атрибутов [NotNull], для подсказки всяким решарперам и прочим. Но решили, что типы NotNull это правильнее. S>Но это касается еще и возвращаемых значений. S>Я в общем то согласен.
так в плюсах ссылка — это указатель, который не null. Этого в шарпе нет и ref — совсем иное
void foo(A *a)
{
// здесь a может быть null и нужно добавлять проверку
}
void foo(A &a)
{
// здесь a не null и можно работать с объектом без доп. проверок
}
Re[4]: C# 8 и null-допустимость - ошибка на миллиард
Здравствуйте, vmpire, Вы писали:
V>Здравствуйте, karbofos42, Вы писали:
K>>А в плюсах вполне себе используют указатели и ссылки и не слышал, чтобы кто-то говорил, что ссылки не нужны и из-за них появляются трудноуловимые ошибки. V>Ну так я как раз и пишу, что не нужно решать несуществующие проблемы
ну, существующие проблемы — везде и всюду пишешь проверку на null в начале методов, лезешь в чужие методы, чтобы посмотреть что он там принимает, а что — нет.
Всё это в итоге падает на этапе выполнения и компилятор ничем не помогает.
Если кто-то что-то не проинициализировал, то немного позже вылезет ошибка, но такого кода, думаю, сильно меньше, чем NRE. С нормальными тестами это ничего не меняет.
Нововведение по сути практически синтаксический сахар. Кому не нравится — можно не использовать. Не вижу проблемы.
Атрибут NotNull слишком многословен и выглядит как костыль.
Re[5]: C# 8 и null-допустимость - ошибка на миллиард
K>так в плюсах ссылка — это указатель, который не null. Этого в шарпе нет и ref — совсем иное
K>
K>void foo(A *a)
K>{
K> // здесь a может быть null и нужно добавлять проверку
K>}
K>void foo(A &a)
K>{
K> // здесь a не null и можно работать с объектом без доп. проверок
K>}
K>
в .Net есть два вида объектов ValueType (инт байт и прочее) и Reference type.
String это аналог вашего *char
String s=null это аналог
*char s=null;
Вызвав
foo(&s)
будет передан адрес переменной s в которой содержится null
но внутри метода можно установить значение
по сути там будет **char
a="чего то там"
это аналог С# варианта
foo(ref string s)
{
и здесь s== null но мы можем изменить значение переменной.
s= "Чего то там"
}
Суть в том, что если ты передаешь ссылку на валуе тип то у валуе типа не может быть null, а вот у ссылочного может.
Это все варианты с *Тип.
Вот ты и путаешь структуры с указателями на эти структуры.
Просто в C# классы это не структуры и по сути являются указателями на объекты в куче.
И они могут содержать null так же как и *char s=null;
От того, что ты ссылаешься на эту переменную, сама переменная содержит null
и солнце б утром не вставало, когда бы не было меня
Re[6]: C# 8 и null-допустимость - ошибка на миллиард
Здравствуйте, Serginio1, Вы писали:
S>Здравствуйте, karbofos42, Вы писали:
K>>так в плюсах ссылка — это указатель, который не null. Этого в шарпе нет и ref — совсем иное
K>>
K>>void foo(A *a)
K>>{
K>> // здесь a может быть null и нужно добавлять проверку
K>>}
K>>void foo(A &a)
K>>{
K>> // здесь a не null и можно работать с объектом без доп. проверок
K>>}
K>>
S> в .Net есть два вида объектов ValueType (инт байт и прочее) и Reference type.
S>String это аналог вашего *char S> String s=null это аналог S>*char s=null;
S>Вызвав S>foo(&s)
S> будет передан адрес переменной s в которой содержится null S>но внутри метода можно установить значение S>по сути там будет **char S>a="чего то там"
S>это аналог С# варианта
S>foo(ref string s) S>{ S> и здесь s== null но мы можем изменить значение переменной. S> s= "Чего то там" S>}
S>Суть в том, что если ты передаешь ссылку на валуе тип то у валуе типа не может быть null, а вот у ссылочного может. S>Это все варианты с *Тип.
S> Вот ты и путаешь структуры с указателями на эти структуры. S>Просто в C# классы это не структуры и по сути являются указателями на объекты в куче. S>И они могут содержать null так же как и *char s=null;
S>От того, что ты ссылаешься на эту переменную, сама переменная содержит null
И причём здесь вот это вот всё? Как это связано с ссылками в плюсах?
вот накидал пример с одним и тем же объектом в куче: http://cpp.sh/6gkmg
указатель можно и нужно проверять на null внутри метода, а ссылку можно считать корректной. Можно конечно извратиться и всё поломать, но это только специально если сделать.
Меня всегда удивляло почему сразу в шарпе ничего похожего не сделали и заставили везде эти проверки на null писать
Re[7]: C# 8 и null-допустимость - ошибка на миллиард
Здравствуйте, karbofos42, Вы писали:
K>И причём здесь вот это вот всё? Как это связано с ссылками в плюсах? K>вот накидал пример с одним и тем же объектом в куче: K>http://cpp.sh/6gkmg
Да та же проблема, только хуже.
K>указатель можно и нужно проверять на null внутри метода,
Можно, да, как и в шарпе. Нужно — нет, не всегда, а иногда даже вредно.
Вот например код:
K>а ссылку можно считать корректной. Можно конечно извратиться и всё поломать, но это только специально если сделать.
Да без всяких извратов — любое разыменование указателя может потенциально создать поломанную ссылку. Вот тут у тебя:
foo2(*obj);
Ты забыл проверить obj на null при разыменовании. Конечно, в трёх срочках легко поглядеть глазками и увидеть что obj всегда безусловно присваивается, а в реальном коде строчек будет поболее, немного условных операторов и всё становится неочевидно. А компилятор никак тебе не поможет — в этом и беда.
K>Меня всегда удивляло почему сразу в шарпе ничего похожего не сделали и заставили везде эти проверки на null писать
Потому что это проблему не решает, скорее усугубляет.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Мой взгляд таков, что в C# это не приживется также как и в kotlin.
Есть прекрасная альтернатива в дотнете:
type Person = {
Name : string
Note : string option
}
type Date = System.DateTime
type Id = System.Int32
let report person =
match person.Note with
| None -> printfn "%s не содержит описания" person.Name
| Some note -> printfn "%s (%s)" person.Name note
report {Name = "Alice"; Note = None }
report {Name = "Bob"; Note = Some"Bad programmer" }
report null // error FS0043: Тип "Person" не содержит собственное значение "null"
F#
1. записи
2. алиасы
3. необязательные значения
4. гораздо более приятный и лаконичный синтаксис
5. вообщем много вещей
Для гуя есть реактивный elm шаблон — по каждой команде вычисляется новое состояние и обновляется интерфейс.
под веб есть fable, под десктоп есть шаблон для авалонии.
PS но мне все больше нравится красота и лаконичность кложи и ее динамическая типизация.
Например, множественная диспетчеризация(мультиметоды) обсуждалась еще при проектировании C# 5, но как я понимаю этого до сих пор там нет.
В кложе даже возможна сигнальная обработка ошибок как чистом комон лиспе.
Нужно лишь начать мыслить шире.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[8]: C# 8 и null-допустимость - ошибка на миллиард
В методе g я так же постараюсь добавить проверку на null, если он не поддерживает такие входные данные. В итоге две проверки одного и того же будет.
Если бы в f изначально пришла ссылка, то не потребовалось бы ни одной проверки на null в этих методах.
·>Ты забыл проверить obj на null при разыменовании. Конечно, в трёх срочках легко поглядеть глазками и увидеть что obj всегда безусловно присваивается, а в реальном коде строчек будет поболее, немного условных операторов и всё становится неочевидно. А компилятор никак тебе не поможет — в этом и беда.
не забыл, а опустил этот момент, т.к. здесь в своём коде я точно знал, что указатель не равен null.
Зачем проверять на null указатель, который в начале метода инициализируется?
Если метод ожидает на вход не null-ссылку, то я должен проверить свои данные, а не разбираться в том, что умеет данный метод.
Если метод принимает указатель, то не понятно сразу — можно в него null передать или же нужно сначала в своём коде добавить проверку и не вызывать этот метод, чтобы дальше проверка на null не вывалила NRE.
В итоге проверки на null плодятся по цепочке вызова метода, когда можно было проверить только в одном месте и дальше передать не null ссылку.
K>>Меня всегда удивляло почему сразу в шарпе ничего похожего не сделали и заставили везде эти проверки на null писать ·>Потому что это проблему не решает, скорее усугубляет.
Я вижу только проблемы в коде, который десереализует не nullable свойства, которые никак не проверяются нигде и дальше некие default(T) не уходят бесконтрольно по коду, вызывая странный эффект.
Для этого достаточно десереализуемые свойства объявлять nullable и работать как раньше или настраивать сериализаторы (проставлять Required и т.п.).
Re[4]: C# 8 и null-допустимость - ошибка на миллиард
Здравствуйте, ·, Вы писали: ·>Какое-то плохое объяснение... Чем это принципиально отличается от: ·>Ну вот обратились мы к полю ._xx, а оно — "". Почему оно "" — да х.з. Где вместо "" может быть [], 0 и т.д.
Да, совершенно верно. "Почему _xx — чётно???". "Почему threadMask больше 12?" "Почему lastName не за-урл-енкожен?"
Хорошая, развитая система типов должна давать такие возможности выражать статически.
Увы — имеем ту, которую имеем. Nullable Ref — это попытка заклеить самую большую дыру в системе типов; это не замена полноценному статическому theorem-prover-grade анализу корректности, а попытка, грубо говоря, решить 30% проблем за 0.3% стоимости.
·>По-моему основная проблема, что null насильно добавляет ещё одно значение в область значений всех типов которое в подавляющем большинстве случаев не нужно, но заставляет явно об этом помнить каждый раз, усложняет код часто бессмысленными проверками и/или грозит падением. Притом, если скажем int/string/etc можно обернуть в класс и заставить его быть только в определённом диапазоне, то с null такой трюк не пройдёт, выразить кодом, что null тут нет или есть — не получится.
Ну, тут тоже не всё совсем так. Завёртывание в свой класс работает далеко не всегда — ну, потому что, например, сумма двух intLessThan14 даёт не инт и не intLessThan14, а intLessThan28, а это в терминах классов CLR не выразишь.
Но в целом — да, без non-null это всё вообще бессмысленно.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: C# 8 и null-допустимость - ошибка на миллиард
S>>String это аналог вашего *char S>> String s=null это аналог S>>*char s=null;
S>>Вызвав S>>foo(&s)
S>> будет передан адрес переменной s в которой содержится null S>>но внутри метода можно установить значение S>>по сути там будет **char S>>a="чего то там"
S>>это аналог С# варианта
S>>foo(ref string s) S>>{ S>> и здесь s== null но мы можем изменить значение переменной. S>> s= "Чего то там" S>>}
S>>Суть в том, что если ты передаешь ссылку на валуе тип то у валуе типа не может быть null, а вот у ссылочного может. S>>Это все варианты с *Тип.
S>> Вот ты и путаешь структуры с указателями на эти структуры. S>>Просто в C# классы это не структуры и по сути являются указателями на объекты в куче. S>>И они могут содержать null так же как и *char s=null;
S>>От того, что ты ссылаешься на эту переменную, сама переменная содержит null
K>И причём здесь вот это вот всё? Как это связано с ссылками в плюсах? K>вот накидал пример с одним и тем же объектом в куче: K>http://cpp.sh/6gkmg K>указатель можно и нужно проверять на null внутри метода, а ссылку можно считать корректной. Можно конечно извратиться и всё поломать, но это только специально если сделать. K>Меня всегда удивляло почему сразу в шарпе ничего похожего не сделали и заставили везде эти проверки на null писать
Еще раз в C#
void foo(ref А s)
это аналог
void foo(A &a)
Это по сути адрес переменной. Ты не согласен?
Если передается
void foo(*A &a)
то интересует не адрес переменной а её содержимое. Толку то её считать корректно, когда нужно проверять содержимое переменной.
и солнце б утром не вставало, когда бы не было меня