Что должно быть возвращено, если не ноль, когда значение не может быть найдено - PullRequest
4 голосов
/ 03 марта 2012

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

Возьмите следующий метод

List<Customer> GetCustomerListByRoleID(Guid RoleID) {}

В этом случае (и в большинстве множественных методов) легко просто вернуть пустое List<Customer>, если мы не можем найти наше значение и у нас все хорошо.

Однако в случае метода, подобного

Customer GetCustomerByID(Guid CustomerID) {}

Вы не можете позволить себе возвращать пустой массив. Infact все, что вы можете сделать, это вернуть New Customer();, но тогда у вас есть потенциально неинициализированный объект (который вы все еще должны проверить) или null.

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

Ответы [ 4 ]

10 голосов
/ 03 марта 2012

Для случая с одним значением рассмотрим шаблон TryGet

bool TryGetCustomerByID(Guid CustomerID, out Customer customer) { }

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

Customer c;
if (container.TryGetCustomer(id, out c)) {
  ...
} else {
  // Deal with failure
}

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

Customer GetCustomerByID(Guid id) {
  Customer c;
  if (!TryGetCustomerByID(id, out c)) {
    throw new Exception(...);
  }
  return c;
}
0 голосов
/ 03 марта 2012

Почему повторная установка null не рекомендуется?Идея состоит в том, что если тип возвращаемого значения функции T, вы всегда получаете один объект типа T, в то время как null равен определенно не что-то типа T, даже ноль объектов типаT.

В случае getCustomers() вы получаете List<Customer>, что составляет от 0 до n Customer объектов.

В случае getCustomer() вы быкак 0 или 1 Customer объектов.В Haskell вы получаете ADT и Maybe для этого, в Scala у вас есть case-классы и Option.Что вы можете использовать в C #?Ну, есть Возможно реализация в C # , внутри это действительно просто.

С Maybe<Customer> getCustomer() вы гарантированно не передадите его результат как нулевое значение вместо Customer.Вы явно предупреждены: вы можете либо получить клиента, либо не получить клиента.

Почему Maybe работает лучше, чем null?Это потому, что Maybe - это монада со звуковым математическим основанием, а null - нет.List - это тоже монада, это делает его таким удобным.При правильном подходе монады можно комбинировать приятными способами , чтобы вам не приходилось проверять HasValue везде.С null вы застряли с вложенными if s в стиле C для каждого результата функции.

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

0 голосов
/ 03 марта 2012

Вы можете определить статический объект Customer, который представляет нулевого клиента (т. Е. Customer.Name = "NOT_FOUND"), и вернуть этот объект вместо null. Затем в вызывающем методе просто сравните возвращенное значение с этим статическим клиентом.

edit: Хотя я бы предпочел просто вернуть null.

0 голосов
/ 03 марта 2012

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

  • Возврат null
  • Бросить исключение (CustomerNotFoundException или что-то в этом роде)

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

Вы могли бы вернуть Tuple<Customer, bool> или TryGetCustomer - но, честно говоря, я думаю, что оба они слишком сложны для этой ситуации.

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

...