Функции должны возвратить пустой указатель или пустой объект?

Положительные стороны: мощный; позволяет Вам:

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

Отрицательные стороны: мощный; позволяет Вам:

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

Шаблоны значительно увеличивают жизнеспособное пространство дизайна, которое является не обязательно плохой вещью, но оно действительно делает их что намного тяжелее использовать хорошо. Шаблону кода нужен maintainters, кто понимает не только функции языка, но и последствия дизайна функций языка; в сущности это означает, что много групп разработчиков избегают всех кроме самых простых и наиболее узаконенных приложений шаблонов C++.

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

209
задан 7wp 29 July 2010 в 06:27
поделиться

24 ответа

Обычно лучше всего возвращать значение null, если вы хотите указать, что данные недоступны.

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

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

207
ответ дан 23 November 2019 в 04:38
поделиться

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

При использовании шаблона NullObject это будет: -

public UserEntity GetUserById(Guid userId)

{ // Представьте здесь какой-то код для доступа к базе данных .....

 //Check if data was returned and return a null if none found
 if (!DataExists)
    return new NullUserEntity(); //Should I be doing this here instead? return new UserEntity();  
 else
    return existingUserEntity;

}

class NullUserEntity: IUserEntity { public string getFirstName(){ return ""; } ...} 
2
ответ дан 23 November 2019 в 04:38
поделиться

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

2
ответ дан 23 November 2019 в 04:38
поделиться

Я лично возвращаю экземпляр объекта по умолчанию. Причина в том, что я ожидаю, что метод вернет ноль ко многим или ноль к единице (в зависимости от цели метода). Единственная причина, по которой при использовании этого подхода это может быть состояние ошибки любого рода, заключается в том, что метод не возвращал никаких объектов и всегда ожидался (с точки зрения возврата «один ко многим» или единственного числа).

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

3
ответ дан 23 November 2019 в 04:38
поделиться

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

30
ответ дан 23 November 2019 в 04:38
поделиться

Если ваш возвращаемый тип - массив, тогда верните пустой массив, иначе верните ноль.

27
ответ дан 23 November 2019 в 04:38
поделиться

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

Для строк, массивов и коллекций ситуация обычно иная. Я помню некую директиву от MS о том, что методы должны принимать null как «пустой» список, но возвращать коллекции нулевой длины, а не null . То же и со струнами. Обратите внимание, что вы можете объявлять пустые массивы: int [] arr = new int [0];

12
ответ дан 23 November 2019 в 04:38
поделиться

В нашем бизнесе У нас есть 2 основных метода Get:

Для простоты в контексте, если вы сомневаетесь, что они будут такими:

// Returns null if user does not exist
public UserEntity GetUserById(Guid userId)
{
}

// Returns a New User if user does not exist
public UserEntity GetNewOrExistingUserById(Guid userId)
{
}

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

Это позволяет нам получить лучшее из обоих миров в контексте, в котором они используются.

3
ответ дан 23 November 2019 в 04:38
поделиться

Лично я бы вернул значение null, потому что именно так будет действовать уровень DAL / Repository.

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

Самое главное, чтобы ваш DAL был согласован. / Repos Layer, так что вы не запутаетесь, как его использовать.

10
ответ дан 23 November 2019 в 04:38
поделиться

Это зависит от того, что больше всего подходит для вашего случая.

Имеет ли смысл возвращать значение null, например, «такого пользователя не существует»?

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

Или имеет смысл генерировать исключение (а-ля "FileNotFound" "), если вызывающий код запрашивает пользователя с недопустимым идентификатором?

Однако с точки зрения разделения проблем / SRP первые два более правильны. И технически первый является наиболее правильным (но только на волосок) - GetUserById должен отвечать только за одно - получение пользователя. Собственная обработка " Разделение на другую проверку - bool DoesUserExist (id) будет уместным, если вы выберете исключение.

На основе подробных комментариев ниже : если это вопрос проектирования уровня API , этот метод может быть аналогичен OpenFile или ReadEntireFile. Мы «открываем» пользователя из некоторого репозитория и гидратируем объект из полученных данных. В этом случае подходит исключение . Возможно, это не так, но может быть.

Все подходы приемлемы - это просто зависит от более широкого контекста API / приложения.

Разделение на другую проверку - bool DoesUserExist (id) будет уместным, если вы выберете исключение.

На основе подробных комментариев ниже : если это вопрос проектирования уровня API , этот метод может быть аналогичен OpenFile или ReadEntireFile. Мы «открываем» пользователя из некоторого репозитория и гидратируем объект из полученных данных. В этом случае может быть подходящим исключение . Может и не быть, но может быть.

Все подходы приемлемы - это просто зависит от более широкого контекста API / приложения.

44
ответ дан 23 November 2019 в 04:38
поделиться

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

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

3
ответ дан 23 November 2019 в 04:38
поделиться

Лучшее в этом случае возвращает "null" в случае, если такого пользователя нет. Также сделайте свой метод статическим.

Изменить:

Обычно такие методы являются членами некоторого класса «Пользователь» и не имеют доступа к членам его экземпляра. В этом случае метод должен быть статическим, иначе вы должны создать экземпляр «User», а затем вызвать метод GetUserById, который вернет другой экземпляр «User». Согласитесь, это сбивает с толку. Но если метод GetUserById является членом какого-либо класса "DatabaseFactory" - нет проблем оставить его в качестве члена экземпляра.

3
ответ дан 23 November 2019 в 04:38
поделиться

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

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

Если с пропавшим пользователем все в порядке (один из возможных нормальных результатов вызова этой функции), вернуть ноль ....

ИЗМЕНИТЬ: (чтобы ответить на комментарий Адама в другом ответе)

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

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

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

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

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

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

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

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

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

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

11
ответ дан 23 November 2019 в 04:38
поделиться

Простите мой псевдо-php / код.

Я думаю, это действительно зависит от предполагаемого использования результата.

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

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

Пример:

function saveTheRow($prim_key, $data) {
    $row = getRowByPrimKey($prim_key);

    // Populate the data here

    $row->save();
}

Здесь мы видим, что одна и та же последовательность операций обрабатывает все записи этого типа.

Однако, если конечная цель возвращаемого значения - прочитать и что-то сделать с данными, я бы вернул null. Таким образом, я могу очень быстро определить, не были ли возвращены данные, и отобразить соответствующее сообщение пользователю.

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

Пример:

function displayData($row_id) {
    // Logging of the error would happen in this function
    $row = getRow($row_id);
    if($row === null) {
        // Handle the error here
    }

    // Do stuff here with data
}

function getRow($row_id) {
 $row = null;
 try{
     if(!$db->connected()) {
   throw excpetion("Couldn't Connect");
  }

  $result = $db->query($some_query_using_row_id);

  if(count($result) == 0 ) {
   throw new exception("Couldn't find a record!");
  }

  $row = $db->nextRow();

 } catch (db_exception) {
  //Log db conn error, alert admin, etc...
  return null; // This way I know that null means an error occurred
 }
 return $row;
}

Это мое общее правило. Пока все работает хорошо.

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

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

Пример:

function displayData($row_id) {
    // Logging of the error would happen in this function
    $row = getRow($row_id);
    if($row === null) {
        // Handle the error here
    }

    // Do stuff here with data
}

function getRow($row_id) {
 $row = null;
 try{
     if(!$db->connected()) {
   throw excpetion("Couldn't Connect");
  }

  $result = $db->query($some_query_using_row_id);

  if(count($result) == 0 ) {
   throw new exception("Couldn't find a record!");
  }

  $row = $db->nextRow();

 } catch (db_exception) {
  //Log db conn error, alert admin, etc...
  return null; // This way I know that null means an error occurred
 }
 return $row;
}

Это мое общее правило. Пока что это работает хорошо.

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

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

Пример:

function displayData($row_id) {
    // Logging of the error would happen in this function
    $row = getRow($row_id);
    if($row === null) {
        // Handle the error here
    }

    // Do stuff here with data
}

function getRow($row_id) {
 $row = null;
 try{
     if(!$db->connected()) {
   throw excpetion("Couldn't Connect");
  }

  $result = $db->query($some_query_using_row_id);

  if(count($result) == 0 ) {
   throw new exception("Couldn't find a record!");
  }

  $row = $db->nextRow();

 } catch (db_exception) {
  //Log db conn error, alert admin, etc...
  return null; // This way I know that null means an error occurred
 }
 return $row;
}

Это мое общее правило. Пока все работает хорошо.

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

Пример:

function displayData($row_id) {
    // Logging of the error would happen in this function
    $row = getRow($row_id);
    if($row === null) {
        // Handle the error here
    }

    // Do stuff here with data
}

function getRow($row_id) {
 $row = null;
 try{
     if(!$db->connected()) {
   throw excpetion("Couldn't Connect");
  }

  $result = $db->query($some_query_using_row_id);

  if(count($result) == 0 ) {
   throw new exception("Couldn't find a record!");
  }

  $row = $db->nextRow();

 } catch (db_exception) {
  //Log db conn error, alert admin, etc...
  return null; // This way I know that null means an error occurred
 }
 return $row;
}

Это мое общее правило. Пока все работает хорошо.

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

Пример:

function displayData($row_id) {
    // Logging of the error would happen in this function
    $row = getRow($row_id);
    if($row === null) {
        // Handle the error here
    }

    // Do stuff here with data
}

function getRow($row_id) {
 $row = null;
 try{
     if(!$db->connected()) {
   throw excpetion("Couldn't Connect");
  }

  $result = $db->query($some_query_using_row_id);

  if(count($result) == 0 ) {
   throw new exception("Couldn't find a record!");
  }

  $row = $db->nextRow();

 } catch (db_exception) {
  //Log db conn error, alert admin, etc...
  return null; // This way I know that null means an error occurred
 }
 return $row;
}

Это мое общее правило. Пока что это работает хорошо.

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

Пример:

function displayData($row_id) {
    // Logging of the error would happen in this function
    $row = getRow($row_id);
    if($row === null) {
        // Handle the error here
    }

    // Do stuff here with data
}

function getRow($row_id) {
 $row = null;
 try{
     if(!$db->connected()) {
   throw excpetion("Couldn't Connect");
  }

  $result = $db->query($some_query_using_row_id);

  if(count($result) == 0 ) {
   throw new exception("Couldn't find a record!");
  }

  $row = $db->nextRow();

 } catch (db_exception) {
  //Log db conn error, alert admin, etc...
  return null; // This way I know that null means an error occurred
 }
 return $row;
}

Это мое общее правило. Пока все работает хорошо.

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

Пример:

function displayData($row_id) {
    // Logging of the error would happen in this function
    $row = getRow($row_id);
    if($row === null) {
        // Handle the error here
    }

    // Do stuff here with data
}

function getRow($row_id) {
 $row = null;
 try{
     if(!$db->connected()) {
   throw excpetion("Couldn't Connect");
  }

  $result = $db->query($some_query_using_row_id);

  if(count($result) == 0 ) {
   throw new exception("Couldn't find a record!");
  }

  $row = $db->nextRow();

 } catch (db_exception) {
  //Log db conn error, alert admin, etc...
  return null; // This way I know that null means an error occurred
 }
 return $row;
}

Это мое общее правило. Пока что это работает хорошо.

2
ответ дан 23 November 2019 в 04:38
поделиться

If the case of the user not being found comes up often enough, and you want to deal with that in various ways depending on circumstance (sometimes throwing an exception, sometimes substituting an empty user) you could also use something close to F#'s Option or Haskell's Maybe type, which explicitly seperates the 'no value' case from 'found something!'. The database access code could look like this:

public Option<UserEntity> GetUserById(Guid userId)
{
 //Imagine some code here to access database.....

 //Check if data was returned and return a null if none found
 if (!DataExists)
    return Option<UserEntity>.Nothing; 
 else
    return Option.Just(existingUserEntity);
}

And be used like this:

Option<UserEntity> result = GetUserById(...);
if (result.IsNothing()) {
    // deal with it
} else {
    UserEntity value = result.GetValue();
}

Unfortunately, everybody seems to roll a type like this of their own.

3
ответ дан 23 November 2019 в 04:38
поделиться

Больше мяса для измельчения: допустим, мой DAL возвращает NULL для GetPersonByID, как некоторые советуют. Что должен делать мой (довольно тонкий) BLL, если он получает NULL? Передать это значение NULL и позволить конечному потребителю беспокоиться об этом (в данном случае - странице ASP.Net)? Как насчет того, чтобы BLL генерировал исключение?

BLL может использоваться ASP.Net и Win App или другой библиотекой классов - я думаю, что несправедливо ожидать, что конечный потребитель внутренне «знает», что метод GetPersonByID возвращает ноль (если, я полагаю, не используются нулевые типы).

Я считаю, что мой DAL возвращает NULL, если ничего не найдено. ДЛЯ НЕКОТОРЫХ ОБЪЕКТОВ это нормально - это может быть список вещей 0: многие, так что ничего не иметь (например, список любимых книг). В этом случае мой BLL возвращает пустой список. Для большинства отдельных объектов (например, пользователя, учетной записи, счета-фактуры), если у меня их нет, это определенно проблема и вызовет дорогостоящее исключение. Однако, поскольку получение пользователя по уникальному идентификатору, который ранее был предоставлен приложением, всегда должно возвращать пользователя, исключение является «правильным» исключением, поскольку оно является исключительным. Конечный потребитель BLL (ASP.Net, f'rinstance) всегда ожидает, что все будет в порядке, поэтому будет использоваться обработчик необработанных исключений вместо того, чтобы заключать каждый вызов GetPersonByID в блок try-catch.

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

1
ответ дан 23 November 2019 в 04:38
поделиться

Интересный вопрос, и я думаю, что на него нет "правильного" ответа, поскольку он всегда зависит от ответственности вашего кода. Знает ли ваш метод, являются ли обнаруженные данные проблемой или нет? В большинстве случаев ответ - «нет», и именно поэтому возвращение null и предоставление вызывающей возможности обработки этой ситуации является идеальным.

Может быть, хороший способ отличить методы бросания от методов, возвращающих null, - это найти соглашение в вашей команде: Методы которые говорят, что «получили» что-то, должны вызывать исключение, если нечего получить. Методы, которые могут возвращать значение null, можно было бы назвать по-другому, например, «Найти ...».

2
ответ дан 23 November 2019 в 04:38
поделиться

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

Используя Java, нас просят добавить assert exists (object): «Вы не должны попытаться получить доступ к несуществующему объекту »; в начале любого метода, который мог бы возвращать значение null, чтобы выразить« предварительное условие »(я не знаю, что это за слово по-английски).

IMO это действительно непросто, но я использую это в ожидании чего-то лучшего.

3
ответ дан 23 November 2019 в 04:38
поделиться

Я предпочитаю null , поскольку он совместим с оператором объединения с нулем ( ?? ).

4
ответ дан 23 November 2019 в 04:38
поделиться

Я бы сказал, что возвращает null вместо пустого объекта.

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

Я обычно следую этому правилу:

  • Если поиск с помощью операции первичного ключа не дал результатов, throw ObjectNotFoundException.
  • Если по каким-либо другим критериям поиск не дал результатов, return null.
  • Если при поиске по неключевым критериям результат не найден, может быть возвращено несколько объектов вернуть пустую коллекцию.
4
ответ дан 23 November 2019 в 04:38
поделиться

Мы используем CSLA.NET, и он принимает точку зрения, что неудачная выборка данных должна возвращать "пустой" объект. На самом деле это довольно раздражает, поскольку требует соглашения о проверке того, является ли obj.IsNew , а не obj == null .

Как упоминалось ранее, null return значения приведут к сбою кода сразу, уменьшая вероятность проблем со скрытностью, вызванных пустыми объектами.

Лично я считаю null более элегантным.

Это очень распространенный случай, и я ' m удивлен, что люди здесь кажутся удивленными: в любом веб-приложении данные часто выбираются с использованием параметра строки запроса, который, очевидно, может быть изменен, поэтому разработчик должен обрабатывать случаи «не найден».

Вы можете справиться с этим с помощью:

if (User.Exists(id)) {
  this.User = User.Fetch(id);
} else {
  Response.Redirect("~/notfound.aspx");
}

... но это дополнительный вызов базы данных каждый раз, что может быть проблемой на страницах с высоким трафиком. Принимая во внимание:

this.User = User.Fetch(id);

if (this.User == null) {
  Response.Redirect("~/notfound.aspx");
}

... требуется только один звонок.

4
ответ дан 23 November 2019 в 04:38
поделиться

Чтобы указать другие сказал более содержательно ...

Исключения для исключительных обстоятельств

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

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

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

Теперь я скажу, что это общее правило. Бывают моменты, когда вы можете захотеть сломать это. Однако мой опыт и эксперименты с C # (много такого) и Java (немного) научили меня, что обработка исключений на намного дороже с точки зрения производительности, чем обработка предсказуемых проблем с помощью условной логики. . Я говорю о мелодии на 2 или 3 порядка дороже в некоторых случаях. Итак, если это возможно, ваш код может зациклиться,

2
ответ дан 23 November 2019 в 04:38
поделиться

Если возвращенный объект является чем-то, что можно повторить , Я бы вернул пустой объект, чтобы мне не пришлось сначала проверять значение null.

Пример:

bool IsAdministrator(User user)
{
    var groupsOfUser = GetGroupsOfUser(user);

    // This foreach would cause a run time exception if groupsOfUser is null.
    foreach (var groupOfUser in groupsOfUser) 
    {
        if (groupOfUser.Name == "Administrators")
        {
            return true;
        }
    }

    return false;
}
2
ответ дан 23 November 2019 в 04:38
поделиться

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

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
}

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

public void GetUserById(Guid id, UserCallback callback, NotFoundCallback notFound)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
    else
        notFound(); // or notFound.Call();
}

Тот же подход с использованием одного объекта может выглядеть так:

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback.Found(userEntity);
    else
        callback.NotFound();
}

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

6
ответ дан 23 November 2019 в 04:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: