Почему компилятор С# не бросает для логических сравнений null?

Вчера я завтракал с другом, и они жаловались на null на С#. Он заявил, что null нелогичен. Я решил проверить его претензии, поэтому я проверил несколько простых логических предложений:

Console.WriteLine(null == null); //True
//Console.WriteLine(null == !!null); //BOOM

Console.WriteLine(10 >= null); //False
Console.WriteLine(10 <= null); //False

Console.WriteLine(!(10 >= null)); //True
Console.WriteLine(!(10 <= null)); //True

Проверка равенства кажется простой, и это то, чего я ожидал бы. Тем не менее, чем больше/меньше заявлений, тем логически противоречивы, которые я считаю очень запутанными! Не должны ли эти броски? Операция отрицания срабатывает так, как вы ожидали.

Если я попытаюсь выполнить сравнения (помимо равенства), используя null в Ruby или Python, я получаю ошибку типа в строках "невозможно сравнивать число с nil". Почему С# не делает этого?

Ответ 1

Отличный вопрос.

Старайтесь не думать о null значении, а о "ничего не видеть здесь". Документация определяет null как

Ключевое слово null - это литерал, представляющий нулевую ссылку, которая не относится ни к одному объекту.

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

При этом факт, что 10 >= null и 10 <= null являются false, не является, строго говоря, противоречием - в конце концов, null буквально ничто. Если вы сказали, что 10 >= (some actual thing) и 10 <= (some actual thing) были ложными, то ясно, что это было бы противоречиво, но вы не можете точно иметь противоречие в классическом смысле без какого-либо фактического объекта. Аристотельское определение закона от метафизики таково:

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

Итак, у нас в некотором смысле есть "лазейка". По крайней мере, поскольку Аристотель сформулировал Закон о противоречии, он конкретно ссылался на объекты. Разумеется, на данный момент существует множество интерпретаций Закона о противоречии.

Теперь, обратившись к случаю 10 + null. Можно сказать, что это то же самое, что и null + 10 если это проще. В некотором смысле, то, что должно произойти в этот момент - следует null "поглотить" на 10, или мы не должны просто сказать: "10 + (вообще ничего) действительно должен только равный 10"? Честно говоря, у меня нет очень убедительного ответа здесь с логической точки зрения, кроме того, что "хорошо, такой выбор дизайна". Я подозреваю, что разработчики языка хотели отличить 10 + null от 10 + 0, но у меня нет документации, подтверждающей это. (Действительно, было бы немного странно, если бы они были одинаковыми, ведь 0 - это фактическое значение, которое может быть построено из натуральных чисел, но null - " null иное").

Ответ 2

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

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

10 должно быть больше или меньше нуля... не так ли?

Нет. "Ключевое слово null - это литерал, представляющий нулевую ссылку, которая не ссылается на какой-либо объект", и в соответствии с правилами C# компилятор сканирует доступные операторы для предоставленного типа и если один из участников имеет null результат выражение будет равно null.

Посмотрите на снятых операторов

Также, как 10 + null может быть нулевым?

См. Выше.

Ответ 3

Это помогает, если вы не думаете о "нулевом" качестве ценности - это понятие ничтожества или недостаток стоимости. Это не просто С# - вы столкнетесь с тем же в SQL (NULL + 'asdf' приведет к NULL.)

Так что материал вроде (NULL <10) сам по себе не имеет наиболее логичного смысла - вы сравниваете понятие ничтожности с реальным числом. Технически, нет, ничто не меньше 10. Оно не больше 10. Поэтому в обоих случаях он возвращает false.

Причина, по которой вас предупреждают от NULL, не имеет ничего общего с логикой. Вот отличный пример того, почему NULL в С# часто приводит к ошибкам/ошибкам:

private NumericalAnalysis AnalyzeNumber(int someValue)
{
    if (someCondition)
        return null;
    return new NumericalAnalysis(someValue);
}

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

NumericalAnalysis instance = AnalyzeNumber(3);
if (instance.IsPrime)
    DoSomething();

... кричит! Вы просто наткнулись на ошибку. Вы вызывали AnalyzeNumber(), но не проверяли, было ли возвращаемое значение null до его использования. В конце концов, AnalyzeNumber() иногда возвращает нулевое значение, поэтому при вызове instance.IsPrime он будет бомбить. Вам буквально нужно было бы "if (myVar == null)" проверять каждый раз, когда была вызвана эта функция, - и если вы забудете, вы просто представили ошибку.

Ответ 4

Я не разработчик языка С#, поэтому (пожимаю плечами), но лично я вижу это как исходный системный сбой, поэтому моя голова может просто принять его как есть.

Причина в том, что логически вы не можете сравнивать детерминированное значение (например, int) с индетерминистским значением (nullable int) без какого-либо преобразования в любом случае. Они действительно 2 разных типа, но современные языки программирования просто пытаются размазать их по-своему.

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

Ответ 5

Я считаю, что это по историческим причинам. Основная мотивация добавления типов значений NULL в С# в версии 2.0 языка заключалась в том, чтобы облегчить боль при работе с нулевыми столбцами sql в ADO.NET. Поэтому добавление нулевых чисел было разработано так, как в sql, и так далее.