Почему проверка на ноль плоха / почему я хочу, чтобы опциональный элемент успешно выполнялся, если он нулевой? - PullRequest
0 голосов
/ 30 октября 2018

Я прочитал больше, чем несколько ответов на подобные вопросы, а также несколько учебных пособий, но ни один из них не решил мою основную путаницу. Я нативный Java-кодер, но я также программировал на Swift.

Почему я хотел бы использовать опциональные компоненты вместо нулей?

Я читал, что меньше проверок и ошибок, но они необходимы или их легко избежать при чистом программировании.

Я также читал, что все ссылки успешны (https://softwareengineering.stackexchange.com/a/309137/227611 и val length = text?.length). Но я бы сказал, что это плохо или неправильно. Если я вызову функцию длины, я ожидаю, что она будет содержать длину. Если этого не произойдет, код должен разобраться с этим прямо сейчас, а не продолжать.

Чего мне не хватает?

Ответы [ 3 ]

0 голосов
/ 30 октября 2018
  1. Предотвращает ошибку во время компиляции. Так что вы не можете случайно передать NULL.

  2. В Java любая переменная может быть нулевой. Таким образом, это становится ритуалом для проверки на ноль перед его использованием. В то время как в быстром, только необязательный может быть нулевым Таким образом, вы должны проверить только необязательные для возможного нулевого значения.

  3. Вы не всегда должны проверять необязательные. Вы можете одинаково хорошо работать с опциями, не разворачивая их. Отправка необязательного метода с нулевым значением не нарушает код.

Может быть и больше, но это те, которые очень помогают.

0 голосов
/ 30 октября 2018

TL / DR: Компилятором также можно избежать недопустимых проверок, которые, как вы говорите, можно избежать при чистом программировании. *1003* И нулевые проверки, которые вы говорите, необходимы, могут быть принудительно установлены компилятором. Необязательные - это конструкция типа, которая делает это возможным.


var length = text?.length

На самом деле это хороший пример того, как полезны дополнительные функции. Если текст не имеет значения, он также не может иметь длину. В Objective-C, если text равен nil, то любое отправленное вами сообщение ничего не делает и возвращает 0. Этот факт иногда был полезен и позволял пропустить большую проверку nil, но также мог привести к незначительным ошибкам .

С другой стороны, многие другие языки указывают на тот факт, что вы отправили сообщение с нулевым указателем, предупредив аварийное завершение сразу же при выполнении этого кода. Это немного облегчает определение проблемы во время разработки, но ошибки времени выполнения не так велики, когда они случаются с пользователями.

Swift использует другой подход: если text не указывает на то, что имеет длину, то не имеет длины . Необязательный не указатель, это тип типа, который либо имеет значение, либо не имеет значения. Можно предположить, что переменная length является Int, но на самом деле это Int?, что является совершенно другим типом.

Если я вызову функцию длины, я ожидаю, что она будет содержать длину. Если этого не произойдет, код должен разобраться с этим прямо сейчас, а не продолжать.

Если text равен nil, то нет объекта для отправки сообщения length, поэтому length даже не вызывается, и результат равен nil. Иногда это хорошо - имеет смысл, что если нет text, то не может быть и length. Вы можете не заботиться об этом - если вы готовились нарисовать символы в text, то тот факт, что нет length, не будет беспокоить вас, потому что рисовать нечего. Необязательный статус text и length заставляет вас иметь дело с тем фактом, что эти переменные не имеют значений в той точке, где вам нужны значения.

Давайте рассмотрим немного более конкретную версию:

var text : String? = "foo"
var length : Int? = text?.count

Здесь text имеет значение, поэтому length также получает значение, но length все еще является необязательным, поэтому в какой-то момент в будущем вам придется проверить, существует ли значение, прежде чем использовать это.

var text : String? = nil
var length : Int? = text?.count

В приведенном выше примере text равен нулю, поэтому length также получает ноль. Опять же, вам придется иметь дело с тем фактом, что и text, и length могут не иметь значений, прежде чем пытаться использовать эти значения.

var text : String? = "foo"
var length : Int = text.count

Угадай, что здесь происходит? Компилятор говорит: О, нет, не надо! , поскольку text является необязательным, а это означает, что любое значение, полученное из него, также должно быть необязательным. Но код указывает length как необязательный Int. Если компилятор укажет на эту ошибку во время компиляции, то он будет намного приятнее , чем когда пользователь укажет на нее намного позже.

var text : String? = "foo"
var length : Int = text!.count

Здесь ! говорит компилятору, что вы думаете, что знаете, что делаете. В конце концов, вы просто присвоили фактическое значение text, так что довольно безопасно предположить, что text не равно нулю. Вы можете написать такой код, потому что вы хотите учесть тот факт, что text может позже стать нулем. Не раскручивайте принудительно дополнительные опции, если вы точно не знаете, потому что ...

var text : String? = nil
var length : Int = text!.count

... если text равно ноль, то вы предали доверие компилятора и заслуживаете ошибку времени выполнения, которую вы (и ваши пользователи) получаете:

error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

Теперь, если text является , а не необязательным, тогда жизнь довольно проста:

var text : String = "foo"
var length : Int = text.count

В этом случае вы знаете , что text и length оба безопасны в использовании без какой-либо проверки, поскольку они не могут быть равны нулю. Вам не нужно быть осторожным, чтобы быть «чистым» - вы буквально не можете присвоить ничего, что не является допустимым String на text, и каждый String имеет count, поэтому length будет получить значение.

Зачем мне когда-либо использовать опционы вместо нулей?

В старые времена Objective-C мы использовали управление памятью вручную. Было небольшое количество простых правил , и если вы неукоснительно следовали правилам, то система подсчета удержания Objective-C работала очень хорошо. Но даже лучшие из нас время от времени оказывались в затруднительном положении, и иногда возникали сложные ситуации, в которых было трудно точно знать, что делать. Огромная часть вопросов Objective-C на StackOverflow и других форумах, связанных с правилами управления памятью. Затем Apple представила ARC (автоматический подсчет хранения), в котором компилятор взял на себя ответственность за сохранение и освобождение объектов, а управление памятью стало на намного проще. Готов поспорить, что здесь меньше 1% вопросов по Objective-C и Swift, касающихся SO, теперь относятся к управлению памятью.

Необязательные опции таковы: они переносят ответственность за отслеживание того, имеет ли переменная значение, имеет или не может иметь значение от программиста к компилятору.

0 голосов
/ 30 октября 2018

Дополнительные опции обеспечивают ясность типа. Int хранит фактическое значение - всегда, тогда как Optional Int (то есть Int?) Хранит либо значение Int, либо ноль. Этот явный «двойной» тип, так сказать, позволяет вам создать простую функцию, которая может четко объявить, что она будет принимать и возвращать. Если ваша функция состоит в том, чтобы просто принимать фактическое Int и возвращать фактическое Int, тогда отлично.

func foo(x: Int) -> Int

Но если ваша функция хочет, чтобы возвращаемое значение было равно нулю, а параметр был равен нулю, она должна сделать это, явно сделав их необязательными:

func foo(x: Int?) -> Int?

В других языках, таких как Objective-C, объекты всегда могут быть нулевыми. Указатели в C ++ тоже могут быть нулевыми. И поэтому любой объект, который вы получаете в Obj-C или любой указатель, который вы получаете в C ++, должен быть проверен на nil, на случай, если это не то, что ожидал ваш код (реальный объект или указатель).

В Swift смысл в том, что вы можете объявлять типы объектов, которые не являются необязательными, и, таким образом, любой код, который вы передаете этим объектам, не должен выполнять никаких проверок. Они могут просто безопасно использовать эти объекты и знать, что они ненулевые. Это часть силы опций Swift. И если вы получаете необязательный, вы должны явно распаковать его в его значение, когда вам нужно получить доступ к его значению. Те, кто кодирует в Swift, стараются всегда делать свои функции и свойства необязательными, когда только могут, если только у них действительно нет причины делать их необязательными.

Еще одна замечательная особенность опций Swift - это все встроенные языковые конструкции для работы с опциями, которые ускоряют процесс написания кода, делают его более читаемым, более компактным ... избавляя от многих хлопот от необходимости проверять и распакуйте опционально и эквивалент того, что вам нужно сделать на других языках.

Отличный пример - оператор слияния ноль (??), а также if-let, guard и многие другие.

Таким образом, дополнительные функции поощряют и обеспечивают более явную проверку типов в вашем коде - проверку типов, которая выполняется компилятором, а не во время выполнения. Конечно, вы можете писать «чистый» код на любом языке, но в Swift это сделать намного проще и более автоматически, во многом благодаря его дополнительным (и не необязательным!).

...