Разумно ли ожидать, что Any () * not * выдаст исключение нулевой ссылки?


41

Когда вы создаете метод расширения, вы, конечно, можете вызывать его в null.But, но в отличие от вызова метода экземпляра, для вызова его в null не нужно бросать NullReferenceException-> вы должны проверять и выбрасывать его вручную.

Для реализации метода расширения Linq Any()Microsoft решила, что они должны добавить ArgumentNullException( https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/AnyAll.cs ).

Меня раздражает необходимость писать if( myCollection != null && myCollection.Any() )

Я не прав, как клиент этого кода, ожидать, что, например, ((int[])null).Any()должен вернуться false?


41
в C # 6 вы можете упростить проверку до if (myCollection? .Any () == true)
17 из 26

5
Даже в F #, который на самом деле не использует пустые значения, за исключением взаимодействия с другими языками .NET, null |> Seq.isEmptyвыбрасывает System.ArgumentNullException: Value cannot be null. Кажется, вы ожидаете, что вы не будете передавать неопределенные значения для чего-то, что, как ожидается, будет существовать, поэтому это все еще исключение, когда у вас есть ноль. Если это вопрос инициализации, я бы начал с пустой последовательности вместо null.
Аарон М. Эшбах

21
Вот почему вы не должны возвращаться nullпри работе с коллекциями, а вместо этого использовать пустую коллекцию в этих случаях.
Бакуриу

31
Ну, почему он вообще нулевой? Вы не хотите, чтобы значение null считалось пустым, потому что оно не пустое, а нечто совершенно иное. Может быть, вы хотите, чтобы он считался пустым в вашем случае, но добавление такого рода вещей в язык приводит к ошибкам.
user253751

22
Я должен отметить, что каждый метод LINQ генерирует нулевой аргумент. Anyпросто соответствует.
Себастьян Редл

Ответы:


157

У меня есть сумка с пятью картошками. В .Any()сумке есть картошка?

« Да », говорите вы.<= true

Я вынимаю всю картошку и ем ее. В .Any()сумке есть картошка?

« Нет », говорите вы.<= false

Я полностью сжигаю сумку в огне. В .Any()сумке сейчас картошка?

" Там нет сумки ".<= ArgumentNullException


3
Но пусто, в сумке нет картошки? Ложь подразумевает Ложь ... (Я не согласен, просто вторая точка зрения).
Д. Бен Кнобл

6
более практично, кто-то вручает вам закрытую коробку из неизвестного источника. В сумке в коробке есть картошка? Меня интересуют только 2 сценария - А) в коробке есть сумка с картошкой, или Б) в коробке нет картошки и / или сумки. Семантически, да, есть разница между нулем и пустым, но 99% времени мне все равно.
Дэйв Тибен

6
Вы съели пять сырых картошек ?? Немедленно обратитесь за медицинской помощью.
Оли

3
@ Только Дэн приготовил их в огне, прежде чем есть и использовать этот огонь, чтобы сжечь сумку.
Исмаэль Мигель

3
@ D.BenKnoble: Не видя багажника моей машины, готовы ли вы засвидетельствовать, что в багажнике моей машины нет тел? Нет? Какая разница между проверкой багажника и не обнаружением чего-либо или проверкой багажника? Разве вы не можете просто дать показания сейчас, поскольку вы бы видели одинаковое количество тел в любом случае? ... в этом разница. Вы не можете поручиться за что-то, если вы не можете на самом деле подтвердить это в первую очередь. Вы предполагаете, что они равны, но это не дано. В некоторых случаях между ними может быть важное различие.
Флэтер

52

Во-первых, кажется, что этот исходный код будет выбрасывать ArgumentNullException, а не NullReferenceException.

Тем не менее, во многих случаях вы уже знаете, что ваша коллекция не равна нулю, потому что этот код вызывается только из кода, который знает, что коллекция уже существует, поэтому вам не нужно будет очень часто ставить там проверку на ноль. Но если вы не знаете, что он существует, то имеет смысл проверить, прежде чем вызывать Any()его.

Я не прав, как клиент этого кода, ожидать, что, например, ((int[])null).Any()должен вернуться false?

Да. Вопрос, который Any()отвечает, "содержит ли эта коллекция какие-либо элементы?" Если эта коллекция не существует, то вопрос сам по себе бессмысленный; он не может ни содержать, ни не содержать ничего, потому что он не существует.


18
@ChrisWohlert: Ваша интуиция меня интригует. Вы также думаете, что семантически несуществующий человек - это пустой человек, чье имя - пустая строка, а возраст равен нулю и так далее? И думаете ли вы, что набор, содержащий набор, nullэквивалентен набору, содержащему пустой набор (и наоборот)?
Руах

17
@ChrisWohlert: очевидно, набор может содержать пустой набор; но вы, кажется, верите этому { null }и { {} }должны быть эквивалентны. Я нахожу это захватывающим; Я не могу относиться к этому вообще.
Руах

9
Стоит ли .Addна nullколлекции каким-то образом создать коллекцию и для нас?
Мэтью

13
@ChrisWohlert "A - это пустой набор. B - набор, содержащий арбуз. Является ли пустой? Да. B пустой? Нет. C пустой? Уххх ..."
user253751

16
Педантичность: это не тот случай, когда в теории множеств несуществующее множество является пустым множеством. Пустой набор существует, и любой несуществующий набор не является им (и действительно, вообще не является ничем, поскольку он не существует).
James_pic

22

Нуль означает отсутствие информации, а не элементов.

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

Если вы возвращаете null в некоторых случаях, вы можете изменить это, чтобы вернуть пустую коллекцию. (В противном случае, если вы находите нулевые значения, возвращаемые библиотечными методами (не вашими), это прискорбно, и я бы обернул их для нормализации.)

Смотрите также https://stackoverflow.com/questions/1191919/what-does-linq-return-when-the-results-are-empty


1
Это nullне значит, что никакие элементы - это вопрос разумного соглашения. Просто думать о родных OLESTRINGS, где он на самом деле делает в виду.
Дедупликатор

1
@Deduplicator, вы говорите о возможности связывания объектов и встраивания Microsoft? Если вы помните, что OLE из 90-х! Если смотреть более в настоящее время, многие современные языки (C #, Java, Swift) предоставляют средства для устранения / искоренения использования null.
Эрик Эйдт

2
@ErikEidt Они делают это, отчасти потому, что nullне означает «недостающую информацию». nullне имеет ни одного значимого толкования. Вместо этого все зависит от того, как вы к этому относитесь. nullиногда используется для «отсутствующей информации», но также и для «без элементов», а также для «произошла ошибка», «неинициализированной» или «конфликтующей информации» или какой-либо произвольной, специальной вещи.
Дерек Элкинс

-1. Это решение принимается разработчиком, а не абстрактными теориями. nullАбсолютно часто используется для обозначения «нет данных». Иногда это имеет смысл. В других случаях нет.
jpmc26

13

Помимо условно-нулевого синтаксиса , есть еще один метод, чтобы облегчить эту проблему: не позволяйте вашей переменной остаться null.

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

public MyResult DoSomething(int a, IEnumerable<string> words)
{
    words = words ?? Enumerable.Empty<string>();

    if (!words.Any())
    {
        ...

Вы можете сделать то же самое, когда извлекаете коллекции из другого метода:

var words = GetWords() ?? Enumerable.Empty<string>();

(Обратите внимание , что в тех случаях , когда у вас есть контроль над функцией , как GetWordsи nullэквивалентно пустой коллекции, предпочтительно , чтобы просто вернуть пустую коллекцию в первую очередь.)

Теперь вы можете выполнить любую операцию по вашему желанию в коллекции. Это особенно полезно, если вам нужно выполнить много операций, которые не будут выполнены, когда выполняется сбор null, и в тех случаях, когда вы получите тот же результат, выполнив циклическое выполнение или запрос пустого перечислимого значения, это позволит ifполностью устранить условия.


12

Являюсь ли я ошибочным, как клиент этого кода, ожидать, что eg ((int []) null) .Any () должен возвращать false?

Да, просто потому, что вы находитесь на C #, и это поведение хорошо определено и задокументировано.

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

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


15
Эта «робастность» часто является иллюзией - если код, который генерирует массив, неожиданно начинает генерировать значения NULL из-за ошибки или другой проблемы, ваш «робастный» код скрывает проблему. Иногда это подходящее поведение, но оно не является безопасным по умолчанию.
GoatInTheMachine

1
@GoatInTheMachine - я не согласен с тем, что это безопасное значение по умолчанию, но вы правы, что это действительно скрывает проблему и что такое поведение иногда нежелательно. По моему опыту, скрывать проблему (или доверять модульным тестам, чтобы поймать проблемы другого кода) лучше, чем разбивать ваше приложение, выдавая неожиданные исключения. Я имею в виду на самом деле - если вызывающий код достаточно сломан, чтобы отправить нули, какова вероятность того, что они обрабатывают исключения правильно?
Теластин

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

1
@GoatInTheMachine - я согласен на многие вещи, но коллекции, как правило, разные. Отношение к нулю как к пустой коллекции не является ужасно противоречивым или удивительным поведением.
Теластин

8
Представьте, что у вас есть массив, который заполняется из документа JSON, полученного через веб-запрос, и в процессе работы у одного из клиентов вашего API внезапно возникает проблема, при которой они отправляют частично недействительный JSON, что заставляет вас десериализовать массив как NULL, вы бы предпочли: в худшем случае код генерировал исключение NULL-ссылки в тот момент, когда поступил первый неверный запрос, который вы увидите в журнале, а затем сразу же скажете клиенту исправить свои ошибочные запросы, ИЛИ ваш код говорит: «о, массив пуст, ничего сделать!" и молча написала «У корзины покупок не было товаров» в БД на несколько недель?
GoatInTheMachine

10

Если повторяющиеся проверки на нуль раздражают вас, вы можете создать свой собственный метод расширения IsNullOrEmpty (), чтобы отразить метод String по этому имени и обернуть вызовы null-check и .Any () в один вызов.

В противном случае решение, упомянутое @ 17 из 26 в комментарии к вашему вопросу, будет короче, чем «стандартный» метод, и будет достаточно понятным для любого, кто знаком с новым нулевым условным синтаксисом.

if(myCollection?.Any() == true)

4
Вы также можете использовать ?? falseвместо == true. Мне бы это показалось более понятным (более чистым), поскольку он обрабатывает только случай null, и на самом деле он не является более многословным. Плюс ==проверки на булевы значения обычно вызывают мой рвотный рефлекс. =)
jpmc26

2
@ jpmc26: я не знаю много о C #. Почему не myCollection?.Any()достаточно?
Эрик Думинил

6
@EricDuminil Из-за способа, которым работает оператор с нулевым условием, myCollection?.Any()эффективно возвращается булево значение, допускающее nullable, а не обычное логическое значение (то есть bool? Вместо bool). Нет неявного преобразования из bool? к bool, и это bool, который нам нужен в данном конкретном примере (для проверки как части ifусловия). Следовательно, мы должны либо явно сравнивать, trueлибо использовать оператор слияния нуля (??).
Стивен Рэндс

@ jpmc26 ?? false труднее читать, чем myCollection? .Any () == true. Независимо от вашего рвотного рефлекса, == true - это то, что вы пытаетесь неявно проверить. ?? false просто добавляет дополнительный шум, и вам все еще нужно оценить, что произойдет, если null, то null == true потерпит неудачу, почти без читателя, даже не подозревая об этом ?..
Райан Лич

2
@RyanTheLeach Мне нужно больше задуматься о том, ==будет ли на самом деле работать. Я уже знаю, что происходит с логическим значением интуитивно, когда оно может быть только trueили false; Мне даже не нужно думать об этом. Но бросьте nullв смесь, и теперь я должен решить, правильно ли это ==. ??вместо этого просто исключается nullслучай, сокращается его до trueи false, что вы уже сразу поняли. Что касается моего рвотного рефлекса, когда я впервые вижу его, мой непосредственный инстинкт - просто удалить его, так как он обычно бесполезен, чего вы не хотите.
jpmc26

8

Являюсь ли я ошибочным, как клиент этого кода, ожидать, что eg ((int []) null) .Any () должен возвращать false?

Если вы задаетесь вопросом об ожиданиях, вы должны думать о намерениях.

null означает что-то очень отличное от Enumerable.Empty<T>

Как упоминалось в ответе Эрика Эйдта , существует разница в значении между nullпустой коллекцией.

Давайте сначала взглянем на то, как они должны использоваться.

В книге « Рекомендации по проектированию фреймворка: условные обозначения , идиомы и шаблоны для многократно используемых библиотек .NET», 2-е издание, написанное архитекторами Microsoft Кшиштофом Квалиной и Брэдом Абрамсом, изложены следующие рекомендации:

X НЕ возвращает нулевые значения из свойств коллекции или из методов, возвращающих коллекции. Вместо этого верните пустую коллекцию или пустой массив.

Представьте себе, что вы вызываете метод, который в конечном итоге получает данные из базы данных: если вы получаете пустой массив, или Enumerable.Empty<T>это просто означает, что ваше пробное пространство пусто, т.е. ваш результат - пустой набор . Однако получение nullв этом контексте означало бы состояние ошибки .

В том же смысле, что и картофельная аналогия Дэна Уилсона , имеет смысл задавать вопросы о ваших данных, даже если они пустые . Но это имеет гораздо меньше смысла, если нет набора .


1
Имеют ли они разные значения, сильно зависит от контекста. Часто для целей любой логики они представляют эквивалентные понятия. Не всегда , но часто.
jpmc26

1
@ jpmc26: Я думаю, смысл этого ответа состоит в том, чтобы сказать, что при написании руководящих принципов для c # они имеют другое значение. Вы всегда можете исправить код, где null и empty одинаковы, если хотите. И в некоторых случаях вы также можете делать то же самое, независимо от того, является ли оно пустым или пустым, но это не означает, что они означают одно и то же.
Крис

@Chris И я говорю, что рекомендации по кодированию от людей, которые разработали язык, не могут использоваться в качестве замены для оценки вашего кода в контексте ваших требований, библиотек, которые вы используете, и других разработчиков, с которыми вы работаете. Идея, что они никогда не будут эквивалентны по контексту, является идеализмом «пирога в небе». Они могут даже не быть эквивалентными для данных в целом, но могут быть эквивалентны для генерации результата в какой-то конкретной функции, которую вы пишете; в этом случае вам нужно поддерживать оба, но убедиться, что они дают одинаковый результат.
jpmc26

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

@ Крис Я имею в виду разные контексты (которые часто соответствуют областям видимости). Рассмотрим функцию для примера. nullи empty может привести к тому же возвращаемому значению для функции, но это не означает, что nullи empty означает абсолютно то же самое в вызывающей области видимости.
jpmc26

3

Есть много ответов, объясняющих, почему nullи пустые разные, и достаточно мнений, пытающихся объяснить, почему к ним следует относиться по-разному или нет. Однако вы спрашиваете:

Являюсь ли я ошибочным, как клиент этого кода, ожидать, что eg ((int []) null) .Any () должен возвращать false?

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

Учитывая, что Any()без предиката, по сути, Count() > 0то, что вы ожидаете от этого фрагмента?

List<int> list = null;
if (list.Count > 0) {}

Или общий:

List<int> list = null;
if (list.Foo()) {}

Я полагаю, вы ожидаете NullReferenceException.

  • Any()это метод расширения, он должен плавно интегрироваться с расширенным объектом, тогда выдача исключения - наименее удивительная вещь.
  • Не каждый язык .NET поддерживает методы расширения.
  • Вы всегда можете позвонить Enumerable.Any(null)и там вы точно ожидаете ArgumentNullException. Это тот же метод, и он должен соответствовать - возможно - почти ВСЕМУ другому в Framework.
  • Доступ к nullобъекту является ошибкой программирования , фреймворк не должен использовать нулевое значение как магическое значение . Если вы используете это таким образом, то это ваша ответственность, чтобы справиться с этим.
  • Это самоуверенно , ты думаешь одно, а я - другое. Рамки должны быть как можно более незавершенными.
  • Если у вас есть особый случай, то вы должны быть последовательными : вы должны принимать все более и более взвешенные решения относительно всех других методов расширения: если Count()решение кажется легким, то Where()это не так. Как насчет Max()? Он выбрасывает исключение для ПУСТОГО списка, разве не для nullодного?

То, что разработчики библиотек делали до LINQ, заключалось в том, чтобы вводить явные методы, когда nullэто допустимое значение (например String.IsNullOrEmpty()), тогда они ДОЛЖНЫ соответствовать существующей философии проектирования . Тем не менее, даже если написать довольно тривиально, два метода EmptyIfNull()и EmptyOrNull()могут быть полезны.


1

Джим должен оставлять картошку в каждой сумке. Иначе я его убью.

У Джима есть сумка с пятью картошками. В .Any()сумке есть картошка?

«Да», говорите вы. <= правда

Итак, Джим живет в этот раз.

Джим достает всю картошку и ест ее. Есть ли в сумке картошка .Any ()?

«Нет», говорите вы. <= ложь

Время убивать Джима.

Джим полностью сжигает сумку в огне. Есть ли в сумке картошка .Any ()?

"Там нет сумки." <= ArgumentNullException

Должен ли Джим жить или умереть? Ну, мы не ожидали этого, поэтому мне нужно решение. Позволить Джиму избежать этой ошибки или нет?

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

public bool Any( [NotNull] List bag ) 

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


0

Если это вас так сильно беспокоит, я предлагаю простой метод расширения.

static public IEnumerable<T> NullToEmpty<T>(this IEnumerable<T> source)
{
    return (source == null) ? Enumerable.Empty<T>() : source;
}

Теперь вы можете сделать это:

List<string> list = null;
var flag = list.NullToEmpty().Any( s => s == "Foo" );

... и флаг будет установлен в false.


2
Более естественно написать returnзаявление как:return source ?? Enumerable.Empty<T>();
Джеппе Стиг Нильсен

Это не отвечает на заданный вопрос.
Кеннет К.

@ KennethK.но, похоже, она решает поставленную проблему.
RQDQ

0

Это вопрос о методах расширения C # и их философии проектирования, поэтому я думаю, что лучший способ ответить на этот вопрос - процитировать документацию MSDN о целях методов расширения :

Методы расширения позволяют вам «добавлять» методы к существующим типам, не создавая новый производный тип, не перекомпилируя или иным образом не изменяя исходный тип. Методы расширения представляют собой особый вид статического метода, но они вызываются так, как если бы они были методами экземпляра в расширенном типе. Для клиентского кода, написанного на C #, F # и Visual Basic, нет очевидной разницы между вызовом метода расширения и методами, которые фактически определены в типе.

...

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

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

Если вы реализуете методы расширения для данного типа, помните следующее:

  • Метод расширения никогда не будет вызван, если он имеет ту же сигнатуру, что и метод, определенный в типе.
  • Методы расширения вводятся в область действия на уровне пространства имен. Например, если у вас есть несколько статических классов, которые содержат методы расширения в одном именованном пространстве имен Extensions, все они будут перенесены в область действия using Extensions;директивой.

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

Другими словами, метод расширения должен действовать точно так же, как и метод экземпляра, поскольку в конечном итоге он может быть создан методом экземпляра некоторым клиентом. И поскольку метод экземпляра должен выдавать, если объект, к которому он вызывается null, так же, как и метод расширения.


* В качестве дополнительного примечания, именно с такой ситуацией столкнулись дизайнеры LINQ: когда был выпущен C # 3.0, уже были миллионы клиентов, которые использовали System.Collections.IEnumerableи System.Collections.Generic.IEnumerable<T>в своих коллекциях, и в foreachциклах. Эти классы возвращаются IEnumeratorобъекты , которые были только два метода Currentи MoveNext, таким образом добавляя дополнительные необходимые методы , например, такие , как Count, Anyи т.д., будет нарушать эти миллионы клиентов. Таким образом, чтобы обеспечить эту функциональность (особенно потому, что она может быть реализована с точки зрения Currentи MoveNextс относительной простотой), они выпустили ее как методы расширения, которые можно применять к любым существующим в настоящее времяIEnumerableэкземпляр и может также быть реализован классами более эффективными способами. Если бы дизайнеры C # решили выпустить LINQ в первый же день, он был бы представлен как методы экземпляра IEnumerable, и они, вероятно, разработали бы какую-то систему для обеспечения реализаций интерфейса этих методов по умолчанию.


0

Я думаю, что существенным моментом здесь является то, что, возвращая false вместо того, чтобы выдавать исключение, вы запутываете информацию, которая может иметь отношение к будущим читателям / модификаторам вашего кода.

Если список может быть пустым, я бы предложил для этого отдельный логический путь, так как в противном случае кто-то в будущем может добавить некоторую основанную на списке логику (например, добавление) к else {} вашего if, что приведет к нежелательное исключение, что у них нет оснований прогнозировать.

Читаемость и удобство сопровождения превосходят «Я должен написать дополнительное условие» каждый раз.


0

Как правило, я пишу большую часть своего кода, предполагая, что вызывающая сторона отвечает за то, чтобы не передавать мне нулевые данные, в основном по причинам, изложенным в « Скажи нет пустому» (и другим подобным постам). Концепция нуля часто не совсем понятна, и поэтому желательно инициализировать ваши переменные заранее, насколько это практически возможно. Предполагая, что вы работаете с разумным API, в идеале вы никогда не должны возвращать нулевое значение, поэтому вам никогда не придется проверять нулевое значение. Смысл нуля, как утверждали другие ответы, - это убедиться, что у вас есть действующий объект (например, «сумка») для работы, прежде чем продолжить. Это не особенность Any, а особенность языкасам. Вы не можете выполнять большинство операций с нулевым объектом, потому что он не существует. Это как если бы я попросил вас доехать до магазина на моей машине, за исключением того, что у меня нет машины, поэтому у вас нет возможности использовать мою машину, чтобы доехать до магазина. Бессмысленно выполнять операцию над чем-то, что буквально не существует.

Существуют практические случаи использования нулевого значения, например, когда у вас буквально нет запрошенной информации (например, если я спросил вас, какого мне возраста, наиболее вероятный вариант ответа на этот вопрос - «Я не знаю»). ", что является нулевым значением). Однако в большинстве случаев вы должны хотя бы знать, инициализированы ли ваши переменные или нет. А если нет, то я бы порекомендовал вам ужесточить свой код. Самое первое, что я делаю в любой программе или функции, это инициализирую значение перед тем, как его использовать. Редко мне нужно проверять наличие нулевых значений, потому что я могу гарантировать, что мои переменные не равны нулю. Это хорошая привычка, и чем чаще вы не забываете инициализировать свои переменные, тем реже вам нужно проверять наличие нуля.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.