Мы заметили, что множество ошибок в нашем программном обеспечении, разработанном на C# (или Java), вызывают исключение NullReferenceException.
Есть ли причина, по которой "null" вообще был включен в язык?
В конце концов, если бы не было «null», у меня не было бы ошибки, верно?
Другими словами, какая функция языка не могла бы работать без null?
-5 голосов? Ну да ладно, хватит за идею, что этот сайт приветствует вопросы программирования любого уровня.
@ Cody Brocious: Сообщество явно полно довольно неприятных людей, которые наказывают тех, кто осмеливается задавать экстремальные вопросы новичкам. Ответ может быть очевиден для вас, но не будет очевиден для тех, кто только начинает программировать.
И это определенно вопрос не для новичков! Null - это пережиток сборки / C, а не существенная особенность!
Есть ли значок для большинства комментариев к вопросу? Потому что должно быть. Кроме того, во время «Иисус переворачивает птицу», говоря людям не грубить. Я люблю вас, ребята.
Хотя это явно вопрос новичка, это все еще определенно актуально. Позор вам за то, что проголосовали против.
Я должен сказать, что редактирование Роба лучше. Теперь это почти разумный вопрос.
Хм, кто-то отредактировал его и значительно прояснил.
Как отмечалось ранее, все, что я сделал, это обновил теги, Rich B отредактировал вопрос заранее. По большей части это может быть вопрос вводного уровня, но я видел, как он появляется на некоторых вводных курсах, поэтому это не причина, по которой кто-то не может задать этот вопрос.
@ blue.tuxedo.myopenid.com: Не важная функция? Попробуйте запрограммировать базу данных без нее. Вам нужно будет обработать null, даже если вы просто собираетесь перевести его на что-то другое.
@Telos, вы запрашиваете возможность выбора, а не null, как показано в C#
Я не могу понять, как этот вопрос получил столько голосов.
Он получил от меня +1, потому что я подумал, что это глупый вопрос, но, если подумать, это действительно интересная тема. Никогда даже не удосужился рассмотреть мир без «нуля».





Я не могу говорить о вашей конкретной проблеме, но похоже, что проблема не в существовании null. Null существует в базах данных, вам нужно каким-то образом учесть это на уровне приложения. Я не думаю, что это единственная причина, по которой он существует в .net, заметьте. Но я думаю, это одна из причин.
-1. В .NET база данных NULL обычно представлена как DBNull.Value вместо null.
Null - чрезвычайно мощная функция. Что делать, если у вас нет ценности? Это NULL!
Одна школа мысли - никогда не возвращать null, другая - всегда. Например, некоторые говорят, что вы должны вернуть действительный, но пустой объект.
Я предпочитаю null, поскольку для меня это более верное указание на то, что это на самом деле. Если я не могу получить объект со своего уровня сохраняемости, мне нужен null. Мне не нужно какое-то пустое значение. Но это я.
Это особенно удобно с примитивами. Например, если у меня есть true или false, но это используется в форме безопасности, где разрешение может быть Разрешено, Запрещено или не установлено. Ну, я хочу, чтобы это не было нулевым. Значит, я могу использовать bool?
Я мог бы рассказать гораздо больше, но я оставлю это здесь.
В ситуации Allow / Deny / NotSet вам нужно перечисление, а не то, что включает null.
Ложь, Истина, FileNotFound. :-)
Allow = True, Deny = False, Not Set = No Value, следовательно, NULL. Это был один простой пример. Да, Нет, Нет данных, Истина Ложь Не определено и т. д. Список можно продолжать и продолжать, он идеально подходит для значений типа bool с нулевым значением, а не для перечисления. Если бы у меня было Allow / Deny / IDontKNow / WhoCares / IAmHungry / NotSet, то я бы использовал enum.
Вы обнаружите, что null - это, возможно, наихудший способ встроить в данные элемент «без информации».
Что, если в этом примере есть ошибка в коде, а программист просто забыл присвоить значение флагу Разрешить / Запретить? В этом случае значение null является ошибкой, оно не означает «НЕ УСТАНОВЛЕНА».
Проголосовали против. Null - это на самом деле 2 концепции / функции: возможность представлять недопустимые состояния программы (бесполезно, imo), возможность представлять необязательность (обязательно). По сути, это не мощно, это глупо.
Null - важное требование любого объектно-ориентированного языка. Любая объектная переменная, которой не была назначена ссылка на объект, должна иметь значение NULL.
Итак, Никки, не могли бы вы объяснить этому невежественному старому дураку, что не так с моим утверждением?
@David: в основном нулевые ссылки не являются существенныйтребование любого языка OO. Одно решение: требовать инициализацию всего при объявлении / построении и использовать шаблон нулевого объекта для всего, что вам нужно, чтобы иметь нулевые / неопределенные / недопустимые / по умолчанию / пустые / любые значения.
Нулевое значение - естественное следствие ссылочных типов. Если у вас есть ссылка, она должна ссылаться на какой-то объект - или быть нулевой. Если бы вы запретили обнуление, вам всегда нужно было бы убедиться, что каждая переменная была инициализирована каким-либо ненулевым выражением - и даже тогда у вас возникли бы проблемы, если бы переменные были прочитаны во время фазы инициализации.
Как бы вы предложили убрать понятие ничтожности?
Это не принципиально. Вы можете потребовать инициализации любой переменной типа ref так же, как с типами значений (не допускающими значения NULL). Хотя это было бы хлопотно.
Предположим, вам требуется инициализировать любую переменную типа ref. Предположим, что это статическое поле, и вы вызываете статический метод для инициализации поля - что, если этот метод смотрит на другое поле, которое еще не было инициализировано? Какую ценность это будет иметь?
Вкратце: вероятно, есть способы обойти это, но они станут все более запутанными, если вы посмотрите на крайние случаи.
В чистом функциональном программировании значения могут быть указаны только через параметры функций и оценки выражений (включая вызовы функций), которые все должны быть указаны до их использования и существуют только в той области, в которой они определены, если только они не зафиксированы в замыкании.
@marxidad: Ага, так что я могу понять функциональные языки без ноля. Я не думаю, что null можно удалить из C# без радикального изменения языка.
Вы можете удалить null из подмножества типов, которые могут обрабатываться литералами (хотя все еще с трудностями), но на самом деле основным кандидатом на C#, строка просто окажется пустой строкой, а не null, что вряд ли станет большой победой. вариант расставить точки было бы неплохо, хотя
X.Exmpty (à la string.Empty or EventArgs.Empty), which would take the place of null references for that type. So I think C# could have potentially lived without null.
@stakx: Так что бы произошло, если бы вы попытались читать, например, из FileStream.Empty?
null, but instead that nullможет be replaced by a type-specific default value. That by itself isn't much of an improvement, but demonstrates that nullмог be abolished. If you can then think of a more reasonable thing to return than throw an exception when reading from a hypothetical FileStream.Empty, который would be the real improvement!
@stakx: Похоже, они очень похожи на меня на очень. С таким же успехом вы можете просто разрешить вызов методов с нулевым значением this и реагировать соответствующим образом ... они в основном эквивалентны. (Фактически, может делает это в IL - вы можете вызвать метод экземпляра не виртуально с нулевым значением this.)
this call (but can't remember where exactly?; +1 for that, anyway). Agreed that the two scenarios are almost identical. What null amounts to from this perspective is a convenience: You don't have to think twice about how you could handle the "no object" case more reasonably. FileStream.Emptyмощь or might not provide the nudge to find better solutions.
@Jon, под названием "недействительность" есть 2 понятия: неинициализированный и необязательный. Это то, что делает эту ошибку перенесенной из Алгола.
@Jon, неинициализированный не требуется, это похоже на запрос возможности отображать недопустимые состояния программы.
@Mark не могли бы вы уточнить? Боюсь, я не понимаю, как чисто функциональная идеология устраняет нулевой тип - вы никогда ничего не инициализируете?
Я думаю, что <code> null </code> полезен, но я согласен с stakx в том, что если бы был введен null (например, <code> String.null, Type.null </code>), некоторые из того, что затрудняет ошибки, связанные с null, исчезли бы сохраняя при этом одобрение программиста (поскольку отменить тип сложно по соглашению).
Если бы у меня были мои druthers, Nullable<T> был бы допустим для ссылочных типов, а ссылочные типы могли бы объявлять методы экземпляра в стиле не виртуальных вызовов требовать. Тогда вызов Length для нулевой строки может вернуть ноль, а не вызывать исключение с нулевой ссылкой (что делает значение по умолчанию для строкового типа допустимой пустой строкой).
«Нулевой» включен в язык, потому что у нас есть типы значений и ссылочные типы. Вероятно, это побочный эффект, но я думаю, он хороший. Это дает нам большую власть над тем, как эффективно управлять памятью.
Почему у нас null? ...
Типы значений хранятся в «стеке», их значение находится непосредственно в этой части памяти (т.е. int x = 5 означает, что ячейка памяти для этой переменной содержит «5»).
С другой стороны, ссылочные типы имеют "указатель" в стеке, указывающий на значение действительный в куче (т.е. строка x = "ello" означает, что блок памяти в стеке содержит только адрес, указывающий на фактическое значение в куче. ).
Нулевое значение просто означает, что наше значение в стеке не указывает на какое-либо фактическое значение в куче - это пустой указатель.
Надеюсь, я объяснил это достаточно хорошо.
Проголосовали против. См. Типы опций на языках семейства ML. Короче говоря, делая ссылки непустыми, вы не теряете общности, вы просто делаете свои программы свободными от NullReferenceException.
Удаление null многого не решит. Вам понадобится ссылка по умолчанию для большинства переменных, установленных при инициализации. Вместо исключений с нулевой ссылкой вы получите неожиданное поведение, потому что переменная указывает на неправильные объекты. По крайней мере, нулевые ссылки быстро выходят из строя, а не вызывают неожиданное поведение.
Вы можете посмотреть на шаблон нулевого объекта, чтобы решить часть этой проблемы.
Я называю это чепухой. Введение ссылочного типа, не допускающего значения NULL, позволит вам выбрать, какой из них вам нужен. OCaml и Haskell имеют ссылки, не допускающие значения NULL, и, тем не менее, применимы ко всем проблемным доменам и вообще не страдают от NullReferenceException ...
OCaml и Haskell полностью отличаются от C# и Java и основаны на разных идеях. Простое удаление null из C# или Java не сделает эти языки более похожими на чистый функциональный язык, такой как Haskell.
Андерс Хейлсберг, «отец C#», только что рассказал об этом в его интервью Computerworld:
For example, in the type system we do not have separation between value and reference types and nullability of types. This may sound a little wonky or a little technical, but in C# reference types can be null, such as strings, but value types cannot be null. It sure would be nice to have had non-nullable reference types, so you could declare that ‘this string can never be null, and I want you compiler to check that I can never hit a null pointer here’.
50% of the bugs that people run into today, coding with C# in our platform, and the same is true of Java for that matter, are probably null reference exceptions. If we had had a stronger type system that would allow you to say that ‘this parameter may never be null, and you compiler please check that at every call, by doing static analysis of the code’. Then we could have stamped out classes of bugs.
Сайрус Наджмабади, бывший инженер-разработчик программного обеспечения в команде C# (теперь работающей в Google), обсуждает эту тему в своем блоге: (1-й, 2-й, 3-й, 4-й). Кажется, что самым большим препятствием для принятия типов, не допускающих значения NULL, является то, что нотация нарушит привычки программистов и базу кода. Примерно 70% ссылок на программы C#, скорее всего, завершатся как не допускающие значения NULL.
Если вы действительно хотите иметь ссылочный тип, не допускающий значения NULL, в C#, вам следует попробовать использовать Спецификация # которое является расширением C#, которое позволяет использовать "!" как знак, не допускающий значения NULL.
static string AcceptNotNullObject(object! s)
{
return s.ToString();
}
Как ни странно, вчера вечером я писал о грязном взломе для реализации некоторых из этого: msmvps.com/blogs/jon_skeet/archive/2008/10/06/… Это действительно не хорошее решение, с различными подводными камнями, но тем не менее интересное (IMO!).
На самом деле все типы должны быть неявными, не допускающими значения NULL, даже ссылочные типы. Обнуляемые типы (типы значений, а также ссылочные типы) должны быть явно отмечены знаком «?».
Небольшая поправка: Сайрус Наджмабади больше не работает в команде C#. Я не осознавал этого, пока не получил от него внутреннее письмо сегодня, здесь, в Google :)
Нулевой в C# в основном является переносом из C++, в котором были указатели, которые ни на что не указывали в памяти (или, скорее, на адрес 0x00). В это интервью Андерс Хейлсберг говорит, что он хотел бы добавить в C# ссылочные типы, не допускающие значения NULL.
Однако Null также имеет законное место в системе типов, как что-то вроде нижний тип (где object - это тип Вверх). В lisp нижний тип - NIL, а в Scala - Nothing.
Можно было бы разработать C# без каких-либо нулей, но тогда вам нужно было бы найти приемлемое решение для тех применений, которые обычно используются для null, например unitialized-value, not-found, default-value, undefined-value и None<T>. Вероятно, программисты на C++ и Java, вероятно, не приняли бы такого решения, если бы им это все-таки удалось. По крайней мере, до тех пор, пока они не увидят, что программы на C# никогда не имеют исключений с нулевым указателем.
Не говоря уже о том, что все, что вам действительно нужно сделать, это переименовать null.
Вам не нужно заменять ключевое слово "null" другим единственным ключевым словом. Вы можете ограничить значение «null», а затем добавить другие ключевые слова, которые будут означать разные вещи. Это может уменьшить количество ошибок нулевой ссылки. Даже в JavaScript есть «undefined» в дополнение к «null». VB6 имел "Пусто", "Ничего" И "Нулевое".
Проголосовали против. Вы запутались между двумя способами использования null: «неинициализированный» бесполезен (цитируя jalf «бессмысленные состояния не должны быть представлены») и отличается от «необязательности», которая требуется.
@bltxd: ... что? Не могли бы вы уточнить
Меня просто удивляет, что некоторые люди (см. Телос выше) все еще путают None и Null. Я в основном не согласен со вторым абзацем: Null НЕТ законного места в современной системе типов. Null - это единственное значение, совместимое со всеми ссылочными типами, None <T> - нет, поскольку оно параметрическое. Это огромная разница. Голос против может быть немного резким, но плакат, кажется, подразумевает, что некоторые люди используют «неинициализированное значение», что неверно.
Если вы создаете объект с переменной экземпляра, являющейся ссылкой на какой-либо объект, какое значение вы бы предложили для этой переменной, прежде чем назначать ей какую-либо ссылку на объект?
Вариантов много. Например, компилятор может запретить использование переменных до того, как они будут явно инициализированы. Или компилятор может использовать конструктор по умолчанию (без параметров) и выдать ошибку, если такой конструктор не найден (например, в C++)
Бывают ситуации, когда нулевой - хороший способ обозначить, что ссылка не была инициализирована. Это важно в некоторых сценариях.
Например:
MyResource resource;
try
{
resource = new MyResource();
//
// Do some work
//
}
finally
{
if (resource != null)
resource.Close();
}
В большинстве случаев это достигается с помощью оператора с использованием. Но узор по-прежнему широко используется.
Что касается вашего исключения NullReferenceException, причину таких ошибок часто легко уменьшить, реализовав стандарт кодирования, в котором все параметры проверяются на достоверность. В зависимости от характера проекта я считаю, что в большинстве случаев достаточно проверить параметры на открытых элементах. Если параметры находятся за пределами ожидаемого диапазона, выдается ArgumentException какого-либо типа или возвращается результат ошибки, в зависимости от используемого шаблона обработки ошибок.
Проверка параметров сама по себе не устраняет ошибки, но любые возникающие ошибки легче обнаружить и исправить на этапе тестирования.
В качестве примечания, Андерс Хейлсберг упомянул отсутствие принудительного применения ненулевого значения как одну из самых больших ошибок в спецификации C# 1.0, и что теперь включить ее «сложно».
Если вы все еще думаете, что статически принудительное ненулевое ссылочное значение имеет большое значение, вы можете проверить язык speC#. Это расширение C#, в котором ненулевые ссылки являются частью языка. Это гарантирует, что ссылке, помеченной как ненулевое, никогда не может быть присвоена пустая ссылка.
Null в том виде, в каком он доступен в C# / C++ / Java / Ruby, лучше всего рассматривать как причуду какого-то неясного прошлого (Algol), которое каким-то образом сохранилось до наших дней.
Вы можете использовать его двумя способами:
Как вы уже догадались, 1) это то, что вызывает у нас бесконечные проблемы в общих императивных языках и должно было быть запрещено давно, 2) это действительно существенная особенность.
Существуют языки, которые избегают 1), но не препятствуют 2).
Например, таким языком является OCaml.
Простая функция, возвращающая постоянно увеличивающееся целое число, начиная с 1:
let counter = ref 0;;
let next_counter_value () = (counter := !counter + 1; !counter);;
Что касается опциональности:
type distributed_computation_result = NotYetAvailable | Result of float;;
let print_result r = match r with
| Result(f) -> Printf.printf "result is %f\n" f
| NotYetAvailable -> Printf.printf "result not yet available\n";;
1 это плохо? Вы говорите, всегда? Как насчет обработки исключений, когда вы хотите увидеть значение переменной в блоке catch? В значительной степени необходимо объявить его вне попытки и инициализировать его внутри, если инициализация может вызвать исключение!
Если ваша конструкция переменной не удалась, ее значение равно нулю.
По крайней мере, в Java вы должны объявлять ссылки без их инициализации: непримитивные поля классов (инициализированные в конструкторе), объекты, которые генерируют исключение во время создания, которое вам нужно использовать вне соответствующего блока try / catch и т. д.
@ blue - И если бы не было "нулевого" значения, не было бы никакого способа указать на сбой, не так ли? Потому что без нулевого значения переменная была бы мусором, и не всегда можно отличить ненулевое значение мусора от хорошего значения.
@PhiLho: объект, который вызвал исключение во время создания, никогда не создается. Конечно, чтобы использовать его впоследствии, вы должны использовать null, но вы также можете отслеживать его успешную инициализацию с помощью логического значения. По сути, ваша логика не требует null, но использует его.
@Onorio: Другое дело - обработка ошибок, и OCaml предоставляет (быстрые) исключения.
Вы слишком упрощаете. Конечно, это могло произойти при инициализации ИЛИ на следующем операторе, и в этом случае переменная будет иметь значимую информацию. Если вы действительно не хотите иметь try / catch для каждой строки кода, вы будете объявлять несколько неинициализированных переменных для использования в catch.
@Telos: Я бы не стал упрощать, если бы привязки базы данных для OCaml не были полностью реализованы и работоспособны. Скептицизм - это нормально, но попробуйте, и вы убедитесь в этом сами. Язык работает по-другому, и неудивительно, что код, который вам придется писать, также будет другим.
@Telos: взгляните на F# (порт Microsoft OCaml на .NET), а точнее на research.microsoft.com/fsharp/manual/spec2.aspx#_Toc20778559 5. На этой странице подробно описывается причина, по которой им пришлось ввести null в F#: технические ограничения в .NET и совместимость с существующими API.
@Telos: У них не было выбора, поскольку они не могли обновить свои устаревшие API и внутреннее устройство. Итак, что мы должны сделать вывод?
@Telos: Удаление null невозможно при жизни в мире C? Все существующие привязки OCaml для библиотек C доказывают обратное.
Если вы получаете исключение NullReferenceException, возможно, вы продолжаете ссылаться на объекты, которые больше не существуют. Это не проблема с "null", это проблема с вашим кодом, указывающим на несуществующие адреса.
Я удивлен, что никто не сказал в ответ о базах данных. В базах данных есть поля, допускающие значение NULL, и любой язык, который будет получать данные из БД, должен это обрабатывать. Это означает наличие нулевого значения.
Фактически, это настолько важно, что для базовых типов, таких как int, вы можете сделать их допускающими значение NULL!
Также рассмотрите возвращаемые значения из функций, что, если бы вы хотели, чтобы функция делила пару чисел, а знаменатель мог бы быть 0? Единственный «правильный» ответ в таком случае будет нулевым. (Я знаю, что в таком простом примере исключение, вероятно, было бы лучшим вариантом ... но могут быть ситуации, когда все значения верны, но действительные данные могут дать неверный или неисчислимый ответ. Не уверен, что в таком случаи...)
Обработка материалов БД означает наличие нулевого значения. Большая разница в том, что в C# у вас NULL использованная литература. Пример разделения, в котором вы не генерируете исключение, уже существует (арифметика с плавающей запятой IEEE 754) и доказывает, что «единственный» правильный ответ - это не пустые ссылки. IEEE 754 использует нулевое значение ценить (NaN - не число).
В одном из ответов упоминалось, что в базах данных есть нули. Это правда, но они сильно отличаются от нулей в C#.
В C# нули - это маркеры для ссылки, которая ни на что не ссылается.
В базах данных пустые значения - это маркеры для ячеек значений, которые не содержат значения. Под ячейками значений я обычно подразумеваю пересечение строки и столбца в таблице, но концепция ячеек значений может быть расширена за пределы таблиц.
На первый взгляд разница между ними кажется тривиальной. Но это не так.
+1 для указания разницы между нулевым использованная литература и значения, что означает, что что-то является нулевым / недействительным / по умолчанию / пустым / чем угодно.
After all, if there were no "null", I would have no bug, right?
Ответ - НЕТ. Проблема не в том, что C# допускает null, проблема в том, что у вас есть ошибки, которые проявляются с помощью NullReferenceException. Как уже было сказано, нули имеют цель в языке обозначать либо «пустой» ссылочный тип, либо не-значение (пусто / ничего / неизвестно).
Удаление null из языка уменьшит вероятность определенных классов ошибок точно так же, как удаление типов указателей снижает вероятность определенных классов ошибок. Если вам действительно нужен null, вы можете использовать небезопасный блок. Типы значений существуют нормально и не допускают значения NULL.
Суть pro3carp3 заключается в том, что большинство нулевых ошибок связано с использованием неинициализированных переменных, как если бы они были подготовлены к использованию. Типы, не допускающие значения NULL, на самом деле решают только случай «Мне все равно, какое значение, я просто хочу запустить этот метод», хотя обычно вам все равно.
Проголосовали против. Ваша предпосылка заключается в том, что вам нужны нули для представления неинициализированных ссылок. Это верно, если вы действительно настаиваете на разрешении неинициализированных ссылок на вашем языке. Но настоящий вопрос заключается в том, имеет ли это смысл, и ответ явно отрицательный. Это сродни требованию для представления недопустимых состояний программы.
bltxd: Ваш комментарий представляет собой наивную точку зрения. Неверные состояния программы действительно возникают по причинам, не зависящим от программистов, таким как ошибки файловой системы, проблемы с подключением к Интернету и т. д. В таких ситуациях действительно имеет смысл использовать значения NULL. Это также бесценно для представления неизвестных, недопустимых и пустых полей базы данных.
Пожалуйста, ознакомьтесь с привязками OCaml или Haskell для PostgreSQL. Они управляют всем, что вы говорите, без повсеместной нулевой ссылки. Короче говоря, существующая практика доказывает, что вы ошибались.
Это хороший момент, инициализированные, но недопустимые объекты просто замаскируют проблемы, а значение null вызовет немедленный сбой.
Null не вызывает исключений NullPointerExceptions ...
Программисты вызывают исключения NullPointerExceptions.
Без нулей мы возвращаемся к использованию фактического произвольного значения, чтобы определить, что возвращаемое значение функции или метода было недопустимым. Вам все равно нужно проверять возвращаемое -1 (или что-то еще), удаление нулей не решит волшебным образом лени, а лишь слегка запутает ее.
Проголосовали против. Попробуйте любой язык из семейства машинного обучения и получите просветление.
только что заметил отрицательный голос ... хотя он, как правило, упускает из виду суть вопроса, поскольку он был специально нацелен на C# и java. Тем не менее, я проверю их, не хочу упустить ключ к раю!
Я предлагаю:
К сожалению, я не думаю, что большинство людей поймет шутку.
Обычно - NullReferenceException означает, что какому-то методу не понравилось то, что ему было передано, и он вернул пустую ссылку, которая позже использовалась без проверки ссылки перед использованием.
Этот метод мог бы показать более подробное исключение вместо возврата null, что соответствует режиму мышления быстро потерпеть неудачу.
Или метод может возвращать null для вашего удобства, чтобы вы могли написать если вместо пытаться и избежать «накладных расходов» исключения.
Помимо ВСЕХ уже упомянутых причин, NULL необходим, когда вам нужен заполнитель для еще не созданного объекта. Например. если у вас есть круговая ссылка между парой объектов, вам понадобится null, поскольку вы не можете создать оба экземпляра одновременно.
class A {
B fieldb;
}
class B {
A fielda;
}
A a = new A() // a.fieldb is null
B b = new B() { fielda = a } // b.fielda isnt
a.fieldb = b // now it isnt null anymore
Обновлено: вы можете вытащить язык, который работает без нулей, но это определенно не будет объектно-ориентированным языком. Например, пролог не имеет нулевых значений.
Выберите существующий объектно-ориентированный язык, например C#. Теперь требуется инициализация всего при объявлении / построении и использовать шаблон нулевого объекта для всего, что вам нужно, чтобы иметь нулевые / неопределенные / недопустимые / по умолчанию / пустые / любые значения.
Вопрос можно интерпретировать как «Лучше иметь значение по умолчанию для каждого типа ссылки (например, String.Empty) или null?». В этом предположении я бы предпочел иметь нули, потому что;
Как и многие вещи в объектно-ориентированном программировании, все восходит к АЛГОЛу. Тони Хоар назвал это своей «ошибкой на миллиард долларов». Во всяком случае, это преуменьшение.
Вот действительно интересный тезис о том, как сделать обнуление не значением по умолчанию в Java. Параллели с C# очевидны.
Котлин нулевой безопасен. Это скорее выбор языка дизайна, который заставляет вас убедиться, что ваши ссылки (или ptrs) на что-то ссылаются.
Являются ли они случаями использования или злоупотребления - это субъективно, но я иногда их использую.
Если фреймворк позволяет создавать массив некоторого типа без указания, что следует делать с новыми элементами, этот тип должен иметь какое-то значение по умолчанию. Для типов, которые реализуют семантику изменяемых ссылок (*), в общем случае нет разумного значения по умолчанию. Я считаю слабым местом платформы .NET отсутствие возможности указать, что вызов невиртуальной функции должен подавлять любую проверку на null. Это позволит неизменным типам, таким как String, вести себя как типы значений, возвращая разумные значения для таких свойств, как Length.
(*) Обратите внимание, что в VB.NET и C# семантика изменяемых ссылок может быть реализована либо классом, либо типом структуры; Тип структуры будет реализовывать семантику изменяемых ссылок, действуя как прокси для обернутого экземпляра объекта класса, на который он содержит неизменяемую ссылку.
Также было бы полезно, если бы можно было указать, что класс должен иметь не допускающую обнуления семантику изменяемого типа значения (подразумевая, что - как минимум - создание экземпляра поля этого типа приведет к созданию нового экземпляра объекта с использованием конструктора по умолчанию, и что копирование поля этого типа приведет к созданию нового экземпляра путем копирования старого (рекурсивная обработка любых вложенных классов типов значений).
Однако неясно, какая именно поддержка должна быть встроена в структуру для этого. Если сама структура распознает различия между изменяемыми типами значений, изменяемыми ссылочными типами и неизменяемыми типами, это позволит классам, которые сами содержат ссылки на смесь изменяемых и неизменяемых типов из внешних классов, эффективно избегать создания ненужных копий глубоко неизменяемых объектов.
Извините за то, что ответил с опозданием на четыре года, я удивлен, что ни один из ответов до сих пор не ответил на исходный вопрос таким образом:
Такие языки, как C# и Java, такие как C и другие предшествующие им языки, есть null, чтобы программист мог писать быстрый, оптимизированный код эффективно использовать указатели.
Сначала немного истории. Причина, по которой был изобретен null, - это эффективность. При низкоуровневом программировании на ассемблере нет абстракции, у вас есть значения в регистрах, и вы хотите максимально использовать их. Определение нуля как значения недействительный указатель - отличная стратегия для представления либо предмет, либо ничего.
Зачем тратить впустую большинство возможных значений совершенно хорошего слова памяти, если у вас может быть очень быстрая реализация шаблона необязательное значение с нулевыми накладными расходами памяти? Вот почему null так полезен.
Семантически null не является необходимым для языков программирования. Например, в классических функциональных языках, таких как Haskell или в семействе ML, нет null, а есть типы с именем Maybe или Option. Они представляют собой более высокоуровневую концепцию необязательное значение, никоим образом не заботясь о том, как будет выглядеть сгенерированный код сборки (это будет работа компилятора).
И это тоже очень полезно, потому что позволяет компилятору использовать поймать больше ошибок, а это означает меньше исключений NullReferenceExceptions.
В отличие от этих языков программирования очень высокого уровня, C# и Java допускают возможное значение null для каждый ссылочный тип (это другое название для тип, который в конечном итоге будет реализован с использованием указателей).
Это может показаться плохим, но что хорошо в этом, так это то, что программист может использовать знания о том, как это работает под капотом, для создания более эффективного кода (даже если в языке есть сборка мусора).
Это причина, по которой null все еще существует в языках сегодня: компромисс между потребностью в общей концепции необязательного значения и вездесущей потребностью в эффективности.
Функция, которая не может работать без нуля, - это возможность представить «отсутствие объекта».
Отсутствие объекта - важное понятие. В объектно-ориентированном программировании он нужен нам для представления ассоциации между объектами, которая является необязательной: объект A может быть присоединен к объекту B, или A может не иметь объекта B. Без нуля мы все равно могли бы эмулировать это: например, мы могли бы использовать список объектов, чтобы связать B с A. Этот список может содержать один элемент (один B) или быть пустым. Это несколько неудобно и на самом деле ничего не решает. Код, который предполагает, что существует B, например aobj.blist.first().method(), взорвется аналогично исключению с нулевой ссылкой: (если blist пуст, каково поведение blist.first()?)
Говоря о списках, null позволяет завершить связанный список. ListNode может содержать ссылку на другой ListNode, которая может иметь значение NULL. То же самое можно сказать и о других структурах динамического набора, таких как деревья. Null позволяет вам иметь обычное двоичное дерево, конечные узлы которого помечены имеющими дочерние ссылки, которые имеют значение NULL.
Списки и деревья могут быть построены без нуля, но они должны быть круговыми или бесконечными / ленивыми. Это, вероятно, будет расценено как неприемлемое ограничение для большинства программистов, которые предпочли бы иметь возможность выбора при разработке структур данных.
Проблемы, связанные с нулевыми ссылками, такими как пустые ссылки, случайно возникающие из-за ошибок и вызывающие исключения, частично являются следствием системы статических типов, которая вводит нулевое значение в каждый тип: есть пустая строка, нулевое целое число, нулевой виджет, ...
В динамически типизированном языке может быть один нулевой объект, который имеет свой собственный тип. В результате у вас есть все репрезентативные преимущества null плюс большая безопасность. Например, если вы пишете метод, который принимает параметр String, то вам гарантируется, что параметр будет строковым объектом, а не null. В классе String нет нулевой ссылки: то, что известно как String, не может быть нулевым объектом. Ссылки не имеют типа на динамическом языке. Место хранения, такое как член класса или параметр функции, содержит значение, которое может быть ссылкой на объект. Этот объект имеет тип, а не ссылку.
Таким образом, эти языки предоставляют чистую, более или менее математически чистую модель «нуля», а затем статические языки превращают ее в своего рода монстра Франкенштейна.
У меня возникло много ошибок, потому что что-то вызвало исключение, можем ли мы их также оставить ...