Я слышал, что включение нулевых ссылок в языки программирования является «ошибкой в миллиард долларов». Но почему? Конечно, они могут вызывать NullReferenceExceptions, но что с того? Любой элемент языка может быть источником ошибок при неправильном использовании.
И какая альтернатива? Я полагаю, вместо того, чтобы сказать это:
Customer c = Customer.GetByLastName("Goodman"); // returns null if not found
if (c != null)
{
Console.WriteLine(c.FirstName + " " + c.LastName + " is awesome!");
}
else { Console.WriteLine("There was no customer named Goodman. How lame!"); }
Вы могли бы сказать это:
if (Customer.ExistsWithLastName("Goodman"))
{
Customer c = Customer.GetByLastName("Goodman") // throws error if not found
Console.WriteLine(c.FirstName + " " + c.LastName + " is awesome!");
}
else { Console.WriteLine("There was no customer named Goodman. How lame!"); }
Но как это лучше? В любом случае, если вы забудете проверить, существует ли клиент, вы получите исключение.
Я полагаю, что CustomerNotFoundException немного легче отлаживать, чем NullReferenceException благодаря большей описательности. Это все, что нужно?
language-design
exceptions
pointers
null
Тим Гудман
источник
источник
Ответы:
ноль это зло
На InfoQ есть презентация на эту тему: « Пустые ссылки: ошибка миллиарда долларов » Тони Хоара
Тип варианта
Альтернативой функциональному программированию является использование типа Option , который может содержать
SOME value
илиNONE
.Хорошая статья Шаблон «Option», в котором обсуждается тип Option и предоставляется его реализация для Java.
Я также нашел сообщение об ошибке для Java об этой проблеме: добавьте типы Nice Option в Java для предотвращения исключений NullPointerExceptions . Запрошенная функция была введена в Java 8.
источник
String
Тип не является на самом деле строкой. ЭтоString or Null
тип. Языки, которые полностью придерживаются идеи типов параметров, запрещают нулевые значения в своей системе типов, давая гарантию, что если вы определяете сигнатуру типа, которая требуетString
(или что-то еще), она должна иметь значение.Option
Типа там , чтобы дать представление типа уровня вещей , которые могут или не могут иметь значение, так что вы знаете , где вы должны явно обрабатывать такие ситуации.Проблема заключается в том, что, поскольку в теории любой объект может быть нулевым и выдавать исключение, когда вы пытаетесь его использовать, ваш объектно-ориентированный код в основном представляет собой набор неразорвавшихся бомб.
Вы правы в том, что изящная обработка ошибок может быть функционально идентична
if
операторам проверки на нуль . Но что происходит, когда то, что вы сами убедили в том, что не может быть нулем, фактически является нулем? Kerboom. Что бы ни случилось дальше, я готов поспорить, что 1) это не будет изящно и 2) вам это не понравится.И не стоит сбрасывать со счетов значение «легко отлаживать». Зрелый производственный код - безумное, растянутое существо; все, что дает вам более полное представление о том, что пошло не так и где может сэкономить часы копания.
источник
public Foo doStuff()
не означает «делать вещи и возвращать результат Foo», это означает «делать вещи и возвращать результат Foo, ИЛИ возвращать ноль, но вы не представляете, произойдет ли это или нет». В результате вы должны проверять NULL везде, чтобы быть уверенным. Можно также удалить пустые значения и использовать специальный тип или шаблон (например, тип значения), чтобы указать бессмысленное значение.Есть несколько проблем с использованием нулевых ссылок в коде.
Во-первых, он обычно используется для обозначения особого состояния . Вместо определения нового класса или константы для каждого состояния, как это обычно делается для специализаций, использование нулевой ссылки - это использование массивно обобщенного типа / значения с потерями .
Во-вторых, отладка кода становится более сложной, когда появляется нулевая ссылка, и вы пытаетесь определить, что сгенерировало ее , какое состояние действует и его причину, даже если вы можете проследить его восходящий путь выполнения.
В-третьих, нулевые ссылки вводят дополнительные пути кода для тестирования .
В-четвертых, как только нулевые ссылки используются в качестве допустимых состояний для параметров, а также возвращаемых значений, защитное программирование (для состояний, вызванных проектированием) требует более тщательной проверки нулевых ссылок в разных местах ... на всякий случай.
В-пятых, среда выполнения языка уже выполняет проверки типов, когда выполняет поиск селектора в таблице методов объекта. Таким образом, вы дублируете усилия, проверяя, является ли тип объекта допустимым / недействительным, а затем проверяете во время выполнения действительный тип объекта, чтобы вызвать его метод.
Почему бы не использовать шаблон NullObject, чтобы воспользоваться преимуществами проверки во время выполнения, чтобы он вызывал методы NOP, специфичные для этого состояния (в соответствии с интерфейсом обычного состояния), а также устранял бы всю дополнительную проверку нулевых ссылок во всей вашей кодовой базе?
Это требует больше работы , создав класс NullObject для каждого интерфейса, с которым вы хотите представить особое состояние. Но, по крайней мере, специализация изолирована для каждого особого состояния, а не кода, в котором это состояние может присутствовать. Таким образом, количество тестов уменьшается, потому что у вас меньше альтернативных путей выполнения в ваших методах.
источник
[self.navigationController.presentingViewController dismissViewControllerAnimated: YES completion: nil];
время выполнения нет элемента navigationController, он обрабатывает его как последовательность операций без операций, представление не удаляется с дисплея, и вы можете часами сидеть перед Xcode, почесывая голову, пытаясь понять, почему.Нули не так уж плохи, если только вы их не ожидаете. Вы должны явно указать в коде, что вы ожидаете null, что является проблемой языкового дизайна. Учти это:
Если вы попытаетесь вызвать методы для a
Customer?
, вы должны получить ошибку во время компиляции. Причина, по которой другие языки не делают этого (IMO), заключается в том, что они не обеспечивают изменение типа переменной в зависимости от того, в какой области она находится. Если язык может с этим справиться, тогда проблема может быть полностью решена в рамках система типов.Существует также функциональный способ решения этой проблемы с использованием опций-типов и
Maybe
, но я не настолько знаком с этим. Я предпочитаю этот путь, потому что для корректного кода теоретически для правильной компиляции должен быть добавлен только один символ.источник
GetByLastName
возвращает ли null, если не найдено (в отличие от выдачи исключения) - я могу просто увидеть, возвращает ли оноCustomer?
илиCustomer
.Maybe
- AMaybe Customer
- этоJust c
(гдеc
естьCustomer
) илиNothing
. На других языках это называетсяOption
- посмотрите другие ответы на этот вопрос.Customer.FirstName
это типString
(в отличие отString?
). Это реальная выгода - только переменные / свойства, которые не имеют значащего значения ничего, должны быть объявлены как обнуляемые.Существует множество отличных ответов, которые охватывают неприятные симптомы
null
, поэтому я хотел бы привести альтернативный аргумент: « Ноль» - это недостаток системы типов.Цель системы типов состоит в том, чтобы гарантировать, что различные компоненты программы правильно сочетаются друг с другом; хорошо типизированная программа не может «сойти с рельсов» в неопределенное поведение.
Рассмотрим гипотетический диалект Java или любой другой предпочитаемый вами статически типизированный язык, где вы можете назначить строку
"Hello, world!"
для любой переменной любого типа:И вы можете проверить переменные так:
В этом нет ничего невозможного - кто-то может создать такой язык, если захочет. Особое значение не должно быть
"Hello, world!"
- это , возможно, было число 42, кортеж(1, 4, 9)
, или, скажем,null
. Но зачем ты это делаешь? Переменная типаFoo
должна содержать толькоFoo
s - в этом весь смысл системы типов!null
неFoo
больше, чем"Hello, world!"
есть. Хуже того,null
это не значение любого типа, и вы ничего не можете с этим поделать!Программист никогда не может быть уверен, что переменная действительно содержит a
Foo
, и программа не может; во избежание неопределенного поведения, он должен проверять переменные,"Hello, world!"
прежде чем использовать их какFoo
s. Обратите внимание, что проверка строки в предыдущем фрагменте не распространяет тот факт, что foo1 действительно являетсяFoo
-bar
скорее всего, будет иметь свою собственную проверку, просто чтобы быть в безопасности.Сравните это с использованием типа
Maybe
/Option
с сопоставлением с шаблоном:Внутри
Just foo
предложения и вы, и программа точно знаете, что нашаMaybe Foo
переменная действительно содержитFoo
значение - эта информация распространяется по цепочке вызовов иbar
не требует каких-либо проверок. ПосколькуMaybe Foo
это тип, отличный от типаFoo
, вы вынуждены обрабатывать возможность того, что он может содержатьNothing
, так что вы никогда не можете быть обманутыми, если aNullPointerException
. Вы можете гораздо проще рассуждать о своей программе, и компилятор может пропустить нулевые проверки, зная, что все переменные типаFoo
действительно содержатFoo
s. Все побеждают.источник
my Int $variable = Int
, гдеInt
нулевое значение типаInt
?this type only contains integers
в отличие отthis type contains integers and this other value
, вы будете иметь проблему каждый раз , когда вы только хотите целые числа.??
,?.
, и так далее) для того , что языки , как Haskell реализуют , как библиотечные функции. Я думаю, что это более аккуратно.null
/Nothing
распространение ни в коем случае не «особенное», так почему мы должны изучать больше синтаксиса, чтобы иметь его?(Кидаю шляпу в кольцо за старый вопрос;))
Конкретная проблема с нулем заключается в том, что он нарушает статическую типизацию.
Если у меня есть
Thing t
, то компилятор может гарантировать, что я могу позвонитьt.doSomething()
. Ну, UNLESSt
является нулевым во время выполнения. Теперь все ставки сняты. Компилятор сказал, что все в порядке, но гораздо позже я узнаю, чтоt
это НЕ такdoSomething()
. Поэтому вместо того, чтобы доверять компилятору в обнаружении ошибок типа, я должен подождать до времени выполнения, чтобы их перехватить. Я мог бы просто использовать Python!Таким образом, в некотором смысле, null вводит динамическую типизацию в статически типизированную систему с ожидаемыми результатами.
Разница между этим делением на ноль или логарифмом отрицательного и т. Д. Состоит в том, что когда i = 0, это все еще int. Компилятор все еще может гарантировать его тип. Проблема в том, что логика неправильно применяет это значение таким образом, который не разрешен логикой ... но если логика делает это, это в значительной степени определение ошибки.
( Кстати, компилятор может уловить некоторые из этих проблем. Как помечать выражения, как
i = 1 / 0
во время компиляции. Но вы не можете ожидать, что компилятор последует за функцией и будет гарантировать, что все параметры соответствуют логике функции)Практическая проблема заключается в том, что вы выполняете много дополнительной работы и добавляете нулевые проверки, чтобы защитить себя во время выполнения, но что, если вы забудете одну? Компилятор останавливает вас от назначения:
Так почему же он должен позволять присваивать значение (нуль), которое прервет разыменование
s
?Смешивая в коде «нет значения» с «типизированными» ссылками, вы ложитесь дополнительной нагрузкой на программистов. А когда возникают исключения NullPointerException, их отслеживание может занять еще больше времени. Вместо того , чтобы полагаться на статической типизации сказать «это есть ссылка на то , как ожидается,» вы позволяете язык сказать «это вполне может быть ссылка на то , как ожидается.»
источник
*int
либо идентифицируетint
, либоNULL
. Аналогично в C ++ переменная типа*Widget
либо идентифицирует aWidget
, вещь, тип которой происходит отWidget
, либоNULL
. Проблема с Java и производными, такими как C #, заключается в том, что они используют место храненияWidget
для обозначения*Widget
. Решение состоит не в том, чтобы притворяться, что объект*Widget
не должен быть в состоянии удержатьсяNULL
, а в том, чтобы иметь надлежащийWidget
тип места хранения.null
Указатель представляет собой инструментне враг. Вы просто должны использовать их правильно.
Подумайте минутку о том, сколько времени нужно, чтобы найти и исправить типичную ошибку неверного указателя , по сравнению с поиском и исправлением ошибки нулевого указателя. Это легко проверить указатель на
null
. Это PITA для проверки того, указывает ли ненулевой указатель на действительные данные.Если вы все еще не уверены, добавьте в свой сценарий хорошую дозу многопоточности, а затем подумайте еще раз.
Мораль истории: не бросайте детей в воду. И не вините инструменты в аварии. Человек, который изобрел число ноль давным-давно, был довольно умен, так как в тот день вы могли назвать «ничто».
null
Указатель не так далеко от этого.РЕДАКТИРОВАТЬ: хотя
NullObject
шаблон кажется лучшим решением, чем ссылки, которые могут быть нулевыми, он создает проблемы самостоятельно:Ссылка, содержащая a,
NullObject
должна (согласно теории) ничего не делать при вызове метода. Таким образом, тонкие ошибки могут быть внесены из-за ошибочно неназначенных ссылок, которые теперь гарантированно не равны нулю (да!), Но выполняют нежелательное действие: ничего. С NPE это почти очевидно, где проблема лежит. С a,NullObject
который ведет себя как-то (но неправильно), мы вводим риск обнаружения ошибок (слишком) поздно. Это не случайно похоже на проблему недопустимого указателя и имеет аналогичные последствия: ссылка указывает на что-то, что похоже на действительные данные, но не вызывает ошибки данных и может быть трудно отследить. Честно говоря, в этих случаях я бы без промедления предпочел бы NPE, которая немедленно выходит из строя , сейчас,из-за логической ошибки / ошибки в данных, которая неожиданно ударит меня позже по дороге. Помните исследование IBM о том, как стоимость ошибок зависит от того, на каком этапе они обнаруживаются?Понятие бездействия, когда метод вызывается в a
NullObject
, не сохраняется, когда вызывается свойство get или функция, или когда метод возвращает значения черезout
. Допустим, возвращаемое значение является,int
и мы решаем, что «ничего не делать» означает «вернуть 0». Но как мы можем быть уверены, что 0 - это правильное «ничто», которое нужно (не) вернуть? В конце концов, онNullObject
не должен ничего делать, но когда запрашивается значение, он должен как-то реагировать. Сбой не вариант: мы используем,NullObject
чтобы предотвратить NPE, и мы, конечно, не будем торговать с другим исключением (не реализовано, делить на ноль, ...), не так ли? Так как же правильно вернуть ничего, когда нужно что-то вернуть?Я не могу помочь, но когда кто-то пытается применить
NullObject
шаблон к каждой проблеме, это больше похоже на попытку исправить одну ошибку, делая другую. Это, без сомнения, хорошее и полезное решение в некоторых случаях, но, безусловно, не волшебная палочка для всех случаев.источник
null
должен существовать? Можно просто поменять любой языковой дефект: «Это не проблема, просто не делайте ошибку».Maybe T
которая может содержать либо значениеNothing
, либоT
значение. Ключевым моментом здесь является то, что вы вынуждены проверить,Maybe
действительно ли объект содержит,T
прежде чем вам разрешено его использовать, и предоставить план действий на случай, если его нет. И поскольку вы запретили неверные указатели, теперь вам никогда не нужно проверять что-либо, что не являетсяMaybe
.t.Something()
? Или есть какой-то способ узнать, что вы уже сделали проверку, и не заставлять вас делать это снова? Что делать, если вы сделали проверку, прежде чемt
перейти к этому методу? С типом Option компилятор узнает, выполнили ли вы проверку или нет, посмотрев на типt
. Если это Option, вы не проверили его, а если это развернутое значение, значит, у вас есть.Нулевые ссылки являются ошибкой, поскольку они допускают бессмысленный код:
Есть альтернативы, если вы используете систему типов:
Основная идея состоит в том, чтобы поместить переменную в коробку, и единственное, что вы можете сделать, - это распаковать ее, предпочтительно заручиться помощью компилятора, как предложено для Eiffel.
У Haskell это есть с нуля (
Maybe
), в C ++ вы можете использовать,boost::optional<T>
но вы все равно можете получить неопределенное поведение ...источник
Maybe
не встроены в ядро языка, его определение как раз случается быть в стандартной прелюдии:data Maybe t = Nothing | Just t
.T
делятся на другие значения типаT
, вы бы ограничили операцию значениями типа,T
деленными на значения типаNonZero<T>
.Необязательные типы и сопоставление с образцом. Поскольку я не знаю C #, вот фрагмент кода на вымышленном языке под названием Scala # :-)
источник
Проблема с нулями заключается в том, что языки, которые позволяют им, в значительной степени заставляют вас защищаться от него. Требуется много усилий (гораздо больше, чем попытка использовать защитные блоки if), чтобы убедиться, что
Так что, действительно, нулевые значения оказываются дорогостоящими.
источник
Вопрос, в какой степени ваш язык программирования пытается доказать правильность вашей программы, прежде чем она запустится. В статически типизированном языке вы доказываете, что у вас есть правильные типы. Переходя по умолчанию к необнуляемым ссылкам (с необязательными обнуляемыми ссылками), вы можете устранить многие случаи, когда нулевое значение передается, а это не должно быть. Вопрос в том, стоит ли дополнительных усилий при обработке ненулевых ссылок на выгоду с точки зрения правильности программы.
источник
Проблема не столько в нулевом, а в том, что вы не можете указать ненулевой ссылочный тип во многих современных языках.
Например, ваш код может выглядеть так
Когда ссылки на классы передаются, защитное программирование побуждает вас снова проверять все параметры на null, даже если вы только что проверили их на null прямо перед этим, особенно при создании тестируемых модулей многократного использования. Одна и та же ссылка может быть легко проверена на нулевое значение 10 раз в кодовой базе!
Разве не было бы лучше, если бы вы могли иметь нормальный обнуляемый тип, когда получаете вещь, время от времени иметь дело с нулем, а затем передавать его как ненулевой тип всем вашим маленьким вспомогательным функциям и классам, не проверяя на нуль все время?
В этом суть решения Option - не для того, чтобы разрешить вам включать состояния ошибок, но чтобы позволить конструировать функции, которые неявно не могут принимать нулевые аргументы. Является ли реализация типов по умолчанию «обнуляемыми» или «не обнуляемыми», менее важно, чем гибкость наличия обоих инструментов.
http://twistedoakstudios.com/blog/Post330_non-nullable-types-vs-c-fixing-the-billion-dollar-mistake
Это также занесено в список наиболее популярных за 2 место функций C # -> https://visualstudio.uservoice.com/forums/121579-visual-studio/category/30931-languages-c.
источник
None
каждого шага пути.Ноль это зло. Однако отсутствие нуля может быть большим злом.
Проблема в том, что в реальном мире часто возникает ситуация, когда у вас нет данных. Ваш пример версии без нулей все еще может взорваться - либо вы допустили логическую ошибку и забыли проверить Гудмана, либо, возможно, Гудман женился между тем, когда вы проверили и когда вы ее искали. (Это помогает оценить логику, если вы считаете, что Мориарти следит за каждым вашим кодом и пытается сбить вас с толку.)
Что делает поиск Гудман, когда ее там нет? Без нуля вы должны вернуть своего рода клиента по умолчанию - и теперь вы продаете вещи по этому умолчанию.
По сути, все сводится к тому, что более важно, чтобы код работал независимо от того, что или он работает правильно. Если неправильное поведение предпочтительнее, чем отсутствие поведения, то вам не нужны значения NULL. (В качестве примера того, как это может быть правильным выбором, рассмотрим первый запуск Ariane V. Ошибка uncaught / 0 заставила ракету совершить крутой поворот, и саморазрушение сработало, когда она развалилась из-за этого. Значение он пытался вычислить, что на самом деле больше не служит какой-либо цели в тот момент, когда зажигается ракета-носитель - он бы вышел на орбиту, несмотря на обычную процедуру возврата мусора.)
По крайней мере, 99 раз из 100 я бы выбрал использовать нули. Я бы прыгнул от радости, заметив их собственную версию.
источник
null
это единственный (или даже предпочтительный) способ моделирования отсутствия значения.Оптимизировать для наиболее распространенного случая.
Необходимость постоянно проверять на ноль утомительна - вы хотите иметь возможность просто получить объект Customer и работать с ним.
В обычном случае это должно работать просто отлично - вы делаете поиск, получаете объект и используете его.
В исключительном случае , когда вы (случайным образом) ищете клиента по имени, не зная, существует ли эта запись / объект, вам понадобится некоторое указание на то, что это не удалось. В этой ситуации ответ - вызвать исключение RecordNotFound (или позволить провайдеру SQL сделать это за вас).
Если вы находитесь в ситуации, когда вы не знаете, можете ли вы доверять входящим данным (параметру), возможно, из-за того, что они были введены пользователем, вы также можете предоставить «TryGetCustomer (имя, клиент»). шаблон. Перекрестная ссылка с int.Parse и int.TryParse.
источник
Исключения не оценены!
Они там по причине. Если ваш код работает плохо, есть шаблон гениальности, называемый исключением , и он говорит вам, что что-то не так.
Избегая использования пустых объектов, вы скрываете часть этих исключений. Я не зацикливался на примере OP, где он преобразует исключение нулевого указателя в хорошо типизированное исключение, на самом деле это может быть хорошей вещью, поскольку это повышает удобочитаемость. Однако, когда вы принимаете решение типа Option, как указывал @Jonas, вы скрываете исключения.
Чем в вашем производственном приложении вместо исключения будет выброшено нажатие на кнопку выбора пустой опции, ничего не происходит. Вместо нулевого указателя будет выдано исключение, и мы, вероятно, получим отчет об этом исключении (как во многих производственных приложениях, у нас есть такой механизм), и чем мы сможем его исправить.
Делать ваш код пуленепробиваемым, избегая исключений, - плохая идея, делать код пуленепробиваемым, исправляя исключения, и этот путь я бы выбрал.
источник
None
чего-то, что не является Option, в ошибку времени компиляции, и аналогичным образом используетOption
as, как будто оно имеет значение без предварительной проверки, что это ошибка времени компиляции. Ошибки компиляции, безусловно, лучше, чем исключения во время выполнения.s: String = None
, ты должен сказатьs: Option[String] = None
. И еслиs
это опция, вы не можете сказатьs.length
, однако вы можете проверить, что она имеет значение, и извлечь значение одновременно с сопоставлением с шаблоном:s match { case Some(str) => str.length; case None => throw RuntimeException("Where's my value?") }
s: String = null
, но это цена взаимодействия с Java. Обычно мы запрещаем подобные вещи в нашем коде Scala.Nulls
проблематичны, потому что они должны быть явно проверены, но компилятор не может предупредить вас, что вы забыли проверить их. Только длительный статический анализ может сказать вам это. К счастью, есть несколько хороших альтернатив.Выньте переменную из области видимости. Слишком часто
null
используется в качестве заполнителя, когда программист объявляет переменную слишком рано или держит ее слишком долго. Лучший подход - просто избавиться от переменной. Не объявляйте это, пока у вас не будет действительного значения, чтобы вставить туда. Это не такое сложное ограничение, как вы могли бы подумать, и оно делает ваш код намного чище.Используйте шаблон нулевого объекта. Иногда пропущенное значение является допустимым состоянием вашей системы. Нулевой объектный образец - хорошее решение здесь. Это устраняет необходимость в постоянных явных проверках, но все же позволяет вам представлять нулевое состояние. Как утверждают некоторые люди, это не «прохождение ошибок», потому что в этом случае нулевое состояние является допустимым семантическим состоянием. При этом вам не следует использовать этот шаблон, когда нулевое состояние не является допустимым семантическим состоянием. Вы должны просто вывести свою переменную из области видимости.
Используйте Возможно / Вариант. Прежде всего, это позволяет компилятору предупредить вас о том, что вам нужно проверить пропущенное значение, но это не просто заменяет один тип явной проверки другим. Используя
Options
, вы можете связать свой код и продолжить, как будто ваше значение существует, не требуя проверки на самом деле до последней минуты. В Scala ваш пример кода будет выглядеть примерно так:Во второй строке, где мы создаем потрясающее сообщение, мы не делаем явной проверки, был ли найден клиент, а красота в том, что нам это не нужно . Это не до самой последней строки, которая может быть много строк спустя, или даже в другом модуле, где мы явно заботимся о том, что произойдет, если
Option
естьNone
. Мало того, что, если бы мы забыли это сделать, он не проверял бы тип. Даже тогда, это сделано очень естественным способом, без явногоif
заявления.Сравните это с a
null
, где тип проверяет просто отлично, но где вы должны сделать явную проверку на каждом этапе пути, или все ваше приложение взорвется во время выполнения, если вам повезет во время использования вашего упражнения с модульными тестами. Это просто не стоит хлопот.источник
Основная проблема NULL в том, что она делает систему ненадежной. В 1980 году Тони Хоар в газете, посвященной его премии Тьюринга, писал:
С тех пор язык ADA сильно изменился, однако такие проблемы все еще существуют в Java, C # и многих других популярных языках.
Разработчик обязан заключать контракты между клиентом и поставщиком. Например, в C #, как и в Java, вы можете использовать,
Generics
чтобы минимизировать влияниеNull
ссылки, создавая readonlyNullableClass<T>
(две опции):а затем использовать его как
или используйте два стиля стиля с методами расширения C #:
Разница значительна. Как клиент метода вы знаете, чего ожидать. Команда может иметь правило:
Если класс не относится к типу NullableClass, то его экземпляр должен быть не нулевым .
Команда может усилить эту идею, используя Design by Contract и статическую проверку во время компиляции, например, с предварительным условием:
или для строки
Такой подход может значительно повысить надежность приложений и качество программного обеспечения. Проектирование по контракту - это случай логики Хоара , который был заполнен Бертраном Мейером в его знаменитой книге «Построение объектно-ориентированного программного обеспечения» и язык Eiffel в 1988 году, но он недопустимо используется в современной разработке программного обеспечения.
источник
Нет.
Неспособность языка учитывать такие особые значения, как
null
проблема языка. Если вы посмотрите на такие языки, как Kotlin или Swift , которые заставляют автора явно иметь дело с возможностью нулевого значения при каждом столкновении, то опасности нет.В таких языках, как рассматриваемый, язык позволяет
null
быть значением любого -ish типа, но не предоставляет каких-либо конструкций для подтверждения этого позже, что приводит кnull
непредвиденным последствиям (особенно при передаче вам откуда-то еще), и Таким образом, вы получаете сбой. Это зло: позволить вам использовать,null
не заставляя вас иметь дело с ним.источник
По сути, основная идея заключается в том, что проблемы со ссылками на нулевые указатели должны быть обнаружены во время компиляции, а не во время выполнения. Поэтому, если вы пишете функцию, которая принимает некоторый объект, и вы не хотите, чтобы кто-либо вызывал ее с нулевыми ссылками, тогда система типов должна позволять вам указывать это требование, чтобы компилятор мог использовать его, чтобы гарантировать, что вызов вашей функции никогда не будет компилировать, если вызывающая сторона не удовлетворяет вашим требованиям. Это можно сделать разными способами, например, украшая ваши типы или используя типы опций (также называемые в некоторых языках обнуляемыми типами), но, очевидно, это гораздо больше работы для разработчиков компиляторов.
Я думаю, что понятно, почему в 1960-х и 70-х годах разработчик компилятора не пошел олл-ин на реализацию этой идеи, потому что машины были ограничены в ресурсах, компиляторы должны были быть относительно простыми, и никто не знал, насколько это плохо получится 40 годы по линии.
источник
У меня есть одно простое возражение против
null
:Он полностью нарушает семантику вашего кода, внося двусмысленность .
Часто вы ожидаете, что результат будет определенного типа. Это семантически обоснованно. Скажем, вы запросили базу данных для пользователя с определенным
id
, вы ожидаете, что результат будет определенного типа (=user
). Но что, если нет пользователя с таким идентификатором? Одно (плохое) решение: добавлениеnull
значения. Так что результат неоднозначен : либо есть,user
либо естьnull
. Ноnull
это не ожидаемый тип. И вот тут начинается запах кода .Во избежание
null
этого вы делаете свой код семантически понятным.Есть всегда способы вокруг
null
: рефакторинга коллекциям,Null-objects
, иoptionals
т.д.источник
Если семантически возможно будет получить доступ к хранилищу ссылки или типа указателя до того, как будет выполнен код для вычисления значения для него, не существует идеального выбора относительно того, что должно произойти. Наличие в таких местах по умолчанию нулевых ссылок на указатели, которые могут быть свободно прочитаны и скопированы, но которые потерпят неудачу в случае разыменования или индексации, являются одним из возможных вариантов. Другие варианты включают в себя:
Последний вариант может иметь некоторую привлекательность, особенно если язык включает в себя типы, допускающие как nullable, так и non-nullable (можно вызывать специальные конструкторы массива для любых типов, но их нужно вызывать только при создании массивов ненулевых типов), но, вероятно, не было бы осуществимо в то время, когда были изобретены нулевые указатели. Из других вариантов ни один из них не выглядит более привлекательным, чем копирование нулевых указателей, но не разыменование или индексация. Подход № 4 может быть удобным в качестве варианта и должен быть довольно дешевым в реализации, но он, безусловно, не должен быть единственным вариантом. Требование, чтобы указатели по умолчанию указывали на какой-то конкретный действительный объект, гораздо хуже, чем указатели по умолчанию на нулевое значение, которое можно прочитать или скопировать, но не разыменовать или проиндексировать.
источник
Да, NULL - ужасный дизайн в объектно-ориентированном мире. Короче говоря, использование NULL приводит к:
Проверьте это сообщение в блоге для подробного объяснения: http://www.yegor256.com/2014/05/13/why-null-is-bad.html
источник