Deutsch

Можно ли возвращать null из функции?

5175  1 2 3 4 5 6 7 8 все
alex445 патриот13.04.24 21:30
NEW 13.04.24 21:30 
в ответ AlexNek 13.04.24 20:20

Понимаю, неделя выдалась плохая, настроение фиговое, хочется с кем-то поругаться. Бывает.

Самое простое обвинить кого-то, не задумываясь о первопричинах.

Он имел ввиду в хорошем смысле.

#61 
uscheswoi_82 коренной житель14.04.24 04:22
uscheswoi_82
NEW 14.04.24 04:22 
в ответ AlexNek 09.04.24 18:52

Гугл не советует:

Is Returning null a Bad Practice? Returning null can be considered bad practice because the caller has to explicitly handle the null checks and forgetting that can lead to unhandled exceptions.08.11.2023


В StackOverflow советуют так см. https://stackoverflow.com/questions/1626597/should-functio...:

Should functions return null or an empty object?
Returning null is usually the best idea if you intend to indicate that no data is available.

An empty object implies data has been returned, whereas returning null clearly indicates that nothing has been returned.

Additionally, returning a null will result in a null exception if you attempt to access members in the object, which can be useful for highlighting buggy code - attempting to access a member of nothing makes no sense. Accessing members of an empty object will not fail meaning bugs can go undiscovered.


А так вы сами писали раньше в этом форуме про вот такие переменные:int?, String?, итд... т.е. если написать метод вот так, то возращает пустоту без ошибок:

using System;
public class Program {
 public static int? WhatAge() {
   return null;
 }
 public static void Main() {
   Console.WriteLine(WhatAge());
 }
}



А в Си в 90х часто пользовались проверкой NULL, допустим если делать проверку на открытие файла, т.к. в языке Си нет try-catch:

FILE *f = fopen("test.txt", "r");
if (f == NULL) {
    printf("Такой файл не существует!\n");
    return 1;
}



Если посмотреть в Си файл stddef.h, там NULL так описывается, т.е. NULL=0:

#define NULL ((void *)0)
#define NULL 0



Я сам только что затестил в Си:

#include <stdio.h>
int main(int argc, char *argv[]) {
 if(0 == NULL)
   printf("0 == NULL\n");
if(((void *)0) == NULL)
   printf("((void *)0) == NULL\n");
 return 0;
}



Выдаёт в консоли:

0 == NULL
((void *)0) == NULL



С Java уже давно не работал, насколько помню, там при объявление переменных нужно обязательно указать null. Например:

public class MySQLAccess {
    private Connection connect = null;
    private Statement statement = null;
    private PreparedStatement preparedStatement = null;
    private ResultSet resultSet = null;
Если я кому-то отвечаю, это не значит что я ему симпатизирую, каждый остаётся при своём мнение Дневник тяжелобольного инвалида
#62 
alex445 патриот14.04.24 06:47
NEW 14.04.24 06:47 
в ответ uscheswoi_82 14.04.24 04:22, Последний раз изменено 14.04.24 06:48 (alex445)
Is Returning null a Bad Practice? Returning null can be considered bad practice because the caller has to explicitly handle the null checks and forgetting that can lead to unhandled exceptions.08.11.2023

Использование ножей опасно? Да, использование ножей опасно - можно порезаться, а кроме того, ими убивают. Использование ножей требует обучения, и нужно постоянно помнить об острой кромке. Выбросьте все ножи и покупайте всё уже нарезанное.

#63 
alex445 патриот14.04.24 06:56
NEW 14.04.24 06:56 
в ответ uscheswoi_82 14.04.24 04:22, Последний раз изменено 14.04.24 07:02 (alex445)
An empty object implies data has been returned, whereas returning null clearly indicates that nothing has been returned.

Additionally, returning a null will result in a null exception if you attempt to access members in the object, which can be useful for highlighting buggy code - attempting to access a member of nothing makes no sense. Accessing members of an empty object will not fail meaning bugs can go undiscovered.

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


Да и в любом случае формирование пустого объекта требует сначала проверки на налл (или на что там в БД считается за отсутствие объекта). Т.е. ты так и так делаешь проверку на налл, потом преобразуешь налл в пустой объект, потом делаешь проверку на пустой объект. И если ты всю эту херню делаешь в одной функции, то ты смешиваешь бизнес-логику и логику работу с другим слоем (база данных, например). Короче, ты привносишь много работы и мало смысла. Зато внешне это выглядит заумно - много слоёв, паттернов, схем, диаграмм для призентаций. Можно пилить такой проект в разы дольше, чем когда всё устроено проще. Но в реальности твои слои становятся связанными, теряется смысл введения этих слоёв, твои абстракции протекают. Вобщем, говнокод. Только не на уровне джуна, а на уровне сеньёра.

#64 
MrSanders коренной житель14.04.24 12:14
NEW 14.04.24 12:14 
в ответ alex445 14.04.24 06:56

И что самое для Ололёшеньки обидное, все эти страдания по поводу "нельзя использовать null" появились исключительно потому что количество Ололёшо-подобных говнокодеров перешло в качество. Если раньше segfault из-за обращения по 0 адресу было редкостью, то теперь (оценочно) 3/4 ошибок в рантайме из-за дереференса нуль-пойнтера. В моём текущем проекте из анализа логов - примерно половина эксепшенов в логе из-за нулей.

Ничего не поделаешь, развитие. Обезьянок-говнокодеров становится всё больше, надо придумывать им инструменты, которые они не сломают и не убьются. Кухонные ножи им давать нельзя. Хорошо хоть есть откуда эти инструменты тырить. Плохо когда ворующие не понимают что и почему они воруют.

#65 
AlexNek патриот14.04.24 12:18
AlexNek
NEW 14.04.24 12:18 
в ответ MrSanders 13.04.24 21:11
про шарп рассказывать не могу

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


Но "в общем" - запрещаем вообще,

ну так это фактически и осталось для обсуждения, так как приходим к мнению, что всё-таки есть случаи когда нулл удобнее. И не только для возврата из хранения.


Паскаль он один. Виртовский

Насколько я помню он был предназначен для обучения и возвращать можно было только value type.

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

#66 
AlexNek патриот14.04.24 12:27
AlexNek
NEW 14.04.24 12:27 
в ответ alex445 13.04.24 21:30
Он имел ввиду в хорошем смысле.

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

Может быть функция была выстрадана годами, а может еще что, что нам попросту неведомо.


Мне кажется, что в любом случае нужно концентрироваться на проблеме, а не на конкретном человеке.

#67 
alex445 патриот14.04.24 12:41
NEW 14.04.24 12:41 
в ответ MrSanders 14.04.24 12:14, Последний раз изменено 14.04.24 12:43 (alex445)
Если раньше segfault из-за обращения по 0 адресу было редкостью, то теперь (оценочно) 3/4 ошибок в рантайме из-за дереференса нуль-пойнтера. В моём текущем проекте из анализа логов - примерно половина эксепшенов в логе из-за нулей.

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

#68 
alex445 патриот14.04.24 12:47
NEW 14.04.24 12:47 
в ответ AlexNek 14.04.24 12:27
Могу только сказать, что поведение нетипичное.
Может быть функция была выстрадана годами, а может еще что, что нам попросту неведомо.

Да что тут гадать. Он как Печкин - злой, потому что у него доски с парусом нет и фургона, на котором это всё возить можно. )))


Мне кажется, что в любом случае нужно концентрироваться на проблеме, а не на конкретном человеке.

А опыт говорит обратное - "нет человека - нет проблем". ))

#69 
AlexNek патриот14.04.24 12:52
AlexNek
NEW 14.04.24 12:52 
в ответ uscheswoi_82 14.04.24 04:22

По этой ссылке тоже высказываются различные мнения

https://stackoverflow.com/questions/1626597/should-functio...


Единственно, что не удается пока найти подобных рекомендаций в каких то статьях/блогах

"Returning null is usually the best idea if you intend to indicate that no data is available."

Скорее наоборот

https://martinfowler.com/eaaCatalog/specialCase.html

#70 
AlexNek патриот14.04.24 12:54
AlexNek
NEW 14.04.24 12:54 
в ответ alex445 14.04.24 06:56
Проверка на пустой объект - зависит от реализации пустого объекта.

Абсолютно никак не зависит

public static Abc NullAbc = ...

if(x != NullAbc)

if(x != Null)

#71 
AlexNek патриот14.04.24 13:01
AlexNek
NEW 14.04.24 13:01 
в ответ alex445 14.04.24 12:47
А опыт говорит обратное - "нет человека - нет проблем".

Это всё относится к несколько иному контексту спок

Да и опыт немного странный насобирался бебе

#72 
MrSanders коренной житель14.04.24 14:36
NEW 14.04.24 14:36 
в ответ AlexNek 14.04.24 12:18
ну вроде особых отличий в данном случае нет, принцип должен остаться неизменным

Что использовать вместо null зависит от инструментария языка. Что там в шарпе есть, nullable types? Типа "MyData? x" значит что x может быть MyData или null. Или "опциональные" значения? Вроде Optional или Maybe в хаскеле и Optional в яве.

ну так это фактически и осталось для обсуждения, так как приходим к мнению, что всё-таки есть случаи когда нулл удобнее. И не только для возврата из хранения.

"Удобнее" для кого? Изначальный вопрос стоял не как "удобно ли возвращать null". Есть люди, которым удобнее писать C++ на ассемблерных вставках, используя препроцессор. Или на перловских сокращениях. Никто понять не может, но ему удобно. Так что сначала надо определиться, что мы узнать-то хотим. Есть ли случаи, когда Ололёшеньке удобнее вернуть null? Конечно, что тут обсуждать.

Насколько я помню он был предназначен для обучения и возвращать можно было только value type.

Ну, списки в нём были. Ссылки были. И был nil. Борланд расширил, но если не лезть в ООП, (когда они это счастье добавили... в 5-м?) то он от классики недалеко ушёл.

#73 
AlexNek патриот14.04.24 21:06
AlexNek
NEW 14.04.24 21:06 
в ответ MrSanders 14.04.24 14:36
Что там в шарпе есть, nullable types?

да есть

Optional в яве

тоже интересно https://habr.com/en/articles/658457/


"Удобнее" для кого?

ну уж точно не для отдельного человека. Скажем так - что можно написать в правилах оформления кода для команды. И главное, объяснить почему мы хотим так делать.

#74 
Murr патриот14.04.24 23:50
Murr
NEW 14.04.24 23:50 
в ответ AlexNek 14.04.24 12:54

Абсолютно никак не зависит

------

Эээ... на больших ИБМах фортрановские и пл-1-овские компиляторы прописывают память спецкодом. Так что проверка на ==/!= НУЛЛ совсем не проверка на ==/!= 0L...

#75 
MrSanders коренной житель15.04.24 10:12
NEW 15.04.24 10:12 
в ответ AlexNek 14.04.24 21:06

Ну вот, раз nullable типы есть, их и используем. Будет заставлять думать при обращении к объекту.

P.S. Мест, где имеет смысл использовать null-объекты на самом деле мало. Редко когда логичный null-объект сделать получается. В нём же смысл не в том, чтоб его сравнивать с результатом функции, а в том чтобы его можно было просто получить и использовать


ну уж точно не для отдельного человека. Скажем так - что можно написать в правилах оформления кода для команды. И главное, объяснить почему мы хотим так делать.

В шарпе я бы прописал использование nullable типов. Они не мешают вернуть null. Но бьют по рукам при дереференсе. Почему... Потому что обращение к null самая частая ошибка. И мы хотим её ограничить. Потому что уже сам создатель null сказал что говно придумал. Но Ололёшеньки же умнее этого старпёра,он им не указ! :)

Лично мне больше нравится Optional/Maybe, чем nullable типы. Они... сильнее бьют по рукам :) Сильнее бросаются в глаза, что мол, ахтунг! тут может быть неопределённое значение!

#76 
Срыв покровов патриот15.04.24 10:20
NEW 15.04.24 10:20 
в ответ MrSanders 15.04.24 10:12

ты имеешь в виду Nullable<int> вместо int? ?

#77 
MrSanders коренной житель15.04.24 10:58
NEW 15.04.24 10:58 
в ответ Срыв покровов 15.04.24 10:20

Про шарп не могу сказать, надо почитать. Насколько я помню Nullable<T> это и есть тип T? в шарпе, нет?


Обычно nullable Type это расширение типа (T?). При попытке получить его значение компилятор ругается если ты не обратился к нему "специальным образом", который скажет что делать если значения нет (null). Типа
MyObject? x = ....
x.doStuff(); // компилятор орёт

x?.doStuff(); // вызовет doStuff() или вернёт nullable Stuff? если x был null
У x есть все методы класса MyObject

А Optional<T> это "обёртка вокруг T". Его не передашь вместо T (и наоборот). У него нет методов T, только свои. У него есть или значение или "дефолтное значение". В языках с null обычно никто не мешает использовать null как дефолтное значение.


#78 
Murr патриот15.04.24 12:40
Murr
NEW 15.04.24 12:40 
в ответ MrSanders 15.04.24 10:58, Последний раз изменено 15.04.24 12:45 (Murr)

x?.doStuff(); // вызовет doStuff() или вернёт nullable Stuff? если x был null

-------

не будет вызова это шарп


В языках с null обычно никто не мешает использовать null как дефолтное значение.

------

А надо?

мне вот больше нравится отдельный тип

#79 
alex445 патриот15.04.24 15:06
NEW 15.04.24 15:06 
в ответ MrSanders 15.04.24 10:58
Насколько я помню Nullable<T> это и есть тип T? в шарпе, нет?

Да, но нет.


Типа Т? не существует (вместо типа это вроде какая-то условная запись для чего-то в последних версиях языка), а тип Nullable<T> - существует, это дженерик.


Тип int? хоть и тождественен Nullable<int>, но сами эти условные записи как конструкции языка не всегда тождественны.

#80 
1 2 3 4 5 6 7 8 все