Зачем проверять, является ли переменная класса пустой до создания нового объекта в конструкторе?

С предыдущей командой, с которой я работал, всякий раз, когда новый класс Service был создан для обработки бизнес-логики между слоем данных и уровнем представления, было выполнено следующее:

class DocumentService
{
    public DocumentRepository DocumentRepository { get; set; }

    public DocumentService()
    {
         if (DocumentRepository == null) DocumentRepository = new DocumentRepository();
    }
}

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

Зачем это делать? Мне кажется, что это лишний шаг, но я не хочу ничего пропускать и передавать его как плохую практику.

Ответы

Ответ 1

В этом точном контексте: Да, это избыточно.

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

Ответ 2

Ответ Henk, конечно, правильный; Я просто хотел добавить, что у меня есть подозрение, откуда это произошло. Готов поспорить, что в какой-то момент в прошлом кто-то писал:

class DocumentService
{
    private DocumentRespository documentRespository = null;
    public DocumentRepository DocumentRepository 
    { 
        get
        {
            if (documentRepository == null) 
                documentRepository = new DocumentRepository();
            return documentRepository;
        }
    }
    public DocumentService()
    {
    }
}

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

Если люди затем программируют путем вырезания и вставки из существующего кода в новый код, шаблон может распространяться. И довольно скоро появляется миф о том, что так оно и должно быть сделано. Вот почему, я подозреваю, так много программистов на Visual Basic имеют ложное убеждение, что вы должны сказать Set Foo = Nothing, когда вы закончите с Foo; это было необходимо однажды в каком-то сценарии, и теперь люди делают это, даже когда это не необходимо, потому что это просто то, как мы это делаем.

Кстати, вы, вероятно, захотите сказать:

public DocumentRepository DocumentRepository { get; private set; } // note the private

Кажется маловероятным, что вы хотите, чтобы пользователи вашего сервиса могли изменять хранилище "на лету".

Ответ 3

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

Ответ 4

Может быть, оператор == для DocumentRepository перезаписан.

Ответ 5

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

В С# или VB.Net вы не можете контролировать, когда вызывается конструктор базового класса, и нет синтаксиса, позволяющего вам устанавливать элементы базового класса до того, как будет вызван конструктор базового класса, - но там нечего предотвратить IL.

Если вы работаете в такой среде, и созданный вами конструктор на самом деле делает что-то еще с DocumentRepository, то вы можете правильно настроить вещи для следующего класса:

public class IllegalCSharpClass : DocumentService
{
    public IllegalCSharpClass()
    {
        DocumentRepository = new DocumentRepository("B");
        base(); //This is illegal C#, but allowed in IL
    }
}

Я бы не хотел работать на таком рабочем месте, хотя.

Здесь IL для класса:

.class public auto ansi beforefieldinit PlayAreaCS_Con.IllegalCSharpClass
       extends PlayAreaCS_Con.DocumentService
{
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    .maxstack  8
    IL_0008:  ldarg.0
    IL_0009:  ldstr      "B"
    IL_000e:  newobj     instance void PlayAreaCS_Con.DocumentRepository::.ctor(string)
    IL_0013:  call       instance void PlayAreaCS_Con.DocumentService::set_DocumentRepository(class PlayAreaCS_Con.DocumentRepository)
    IL_0018:  nop
    IL_0019:  nop
    IL_0000:  ldarg.0
    IL_0001:  call       instance void PlayAreaCS_Con.DocumentService::.ctor()
    IL_0006:  nop
    IL_0007:  nop
    IL_001a:  ret
  } 

}