Когда я должен защищать от нуля?

Когда я должен защищать аргументы null? В идеале я бы защищал от null всюду, но это очень раздутое и утомительное. Я также отмечаю, что люди не ставят охранников в такие вещи, как AsyncCallback s.

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

Спасибо.

Ответ 1

Одним из подходов, который я использовал много, является шаблон нулевого объекта. Например, если a имеет класс factory, который возвращает разные реализации интерфейса на основе аргумента, а предоставленный аргумент не сопоставляется ни с одной из реализаций, я бы возвратил NullObject, например.

   public interface IFoo{
         void Bar();
   }
   public class NullFoo{
       public void Bar(){
          //null behaviour 
       }
   }
   public class FooFactory{
        public IFoo CreateFoo(int i){
              switch(i){
                  case 1:
                  return new OneFoo();
                  break;
                  case 2:
                  return new TwoFoo();
                  break;
                  default:
                  return new NullFoo();
                  break;
              }
        } 
   }

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

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

Другим способом защиты от нулевых аргументов является использование Предпочтения CodeContract. например.

  public void Foo(Bar x){
      Contract.Requires<ArgumentNullException>( x != null, "x" );
      //access x
  }

Использование Code Contracts позволяет вам запускать статический анализ кода с вашим кодом и ловить ошибки, такие как Foo(null). (подробнее здесь)

Еще одна причина - использовать очень простой общий метод расширения:

public static class Ex
{
    public static void EnsureNotNull<T>(this T t, string argName) where T:class
    {
        if(t == null)
        {
            throw new ArgumentNullException(argName);
        }
    }
}

Затем вы можете проверить свои аргументы следующим образом:

 public void Foo(Bar x, Bar y){
     x.EnsureNotNull("x");
     y.EnsureNotNull("y");
 }

Ответ 2

Когда я должен защищать от нулевых аргументов?

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

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

Это правило не является уникальным для null. Вы всегда должны проверять аргументы, переданные вашим общедоступным методам.

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

Я также отмечаю, что люди не кладут охранников в такие вещи, как AsyncCallbacks.

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

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

который становится очень раздутым и утомительным

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

Теперь это может показаться неважным. Но как только вы начнете получать жалобы от клиентов с уровнем NullReferenceException 5 в вашем стеке, 20 методов будут позже, тогда я думаю, вы начнете видеть преимущества:)

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

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

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

Вы можете немного сократить этот код, используя или создавая общий код, поддерживающий синтаксис утверждения. Вместо синтаксиса if ... throw вы должны написать строку типа Assert.NotNull(arg1, "arg1");. Если вам нужно вдохновение, вы можете посмотреть NUnit framework assertions и ограничения.

Вы также можете посмотреть в Code Contracts API. Он предназначен для проверки предварительных условий, пост-условий и инвариантов (которые являются формальными именами для этих "условий охраны" ). Он также может переместить часть этой проверки из времени выполнения, чтобы скомпилировать время, поэтому вы можете обнаружить, что ошиблись, прежде чем запускать свою программу. Я на самом деле не посмотрел на него, но он также может дать вам более сжатый синтаксис. Правка: Также см. ответ Pencho Ilchev для краткого примера использования части этого API.

Ответ 3

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

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

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

Ответ 4

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

public static <T> T checkNull(T arg) {
    if (arg == null)
        throw new IllegalArgumentException();
    return arg;
}

Это немного упростит ваш код.

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

Edit

Заметил, что этот вопрос был помечен как С#, но вы поняли, что...

Ответ 5

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

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

См. Эта ссылка для хорошего обсуждения этой проблемы.