2013-05-22 11 views
14

z poprzednim zespołem ja pracowałem, gdy nowa klasa Serwis został stworzony do obsługi logiki biznesowej pomiędzy warstwy danych i warstwy prezentacji, coś jak poniżej zostało zrobione:Dlaczego warto sprawdzić, czy zmienna klasy ma wartość NULL przed utworzeniem nowego obiektu w konstruktorze?

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

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

Nigdy nie rozumiałem, dlaczego czek dla null był tam. Jeśli konstruktor jest wywoływany, oznacza to, że ma on wartość null, ponieważ jest to nowa instancja, prawda?

Dlaczego tak się stało? Wydaje mi się, że jest to zbędny krok, ale nie chcę niczego przegapić i przekazać go jako złą praktykę.

+2

Jednym z powodów mogę myśleć: gdzieś w dół drogi, chcesz zrobić to statyczne i zapomnieć o nim w konstruktorze i teraz za każdym razem nowy obiekt zostanie utworzony , Właściwość statyczna zostaje nadpisana. – Corak

+0

Jedyny powód, dla którego mogłem zobaczyć dla tego sprawdzenia, czy istnieją wywołania metod powyżej tego wiersza w konstruktorze, który mógłby ustawić repozytorium. (Rozumiem, że nie ma w twoim przykładzie) – Sayse

+0

Czy własność 'DocumentRepository' jest autoproperty dokładnie tak, jak napisałeś to w pytaniu? Jeśli tak, to faktycznie jest to bezcelowe. Jeśli nie, to nie możemy odpowiedzieć, nie widząc prawdziwej implementacji. –

Odpowiedz

17

W tym kontekście: Tak, jest zbędny.

Nie ma Bezpośrednim powodem tego kodu, to może być po lewej nad od starszej metody lub przewidywanie na realizację z wielu konstruktorów. Ale nie polecałbym używania tego "wzoru" ani nawet trzymania tego kodu.

+3

@DoctorOreo: Sugerowałbym, że jeśli wykonasz niepotrzebne sprawdzanie, rozważasz 'Debugowanie .Asert (DocumentRepository == null); DocumentRepository = new DocumentRepository(); - w ten sposób, jeśli sprawdzenie naprawdę * było * sensowne w jakimś dziwnym scenariuszu, o którym nie wiesz, wkrótce się o tym dowiesz. –

+0

Twierdzenie zasugerowane przez @EricLippert jest również sposobem dla scenarzysty na udokumentowanie swoich oczekiwań co do zachowania kodu. Ta dokumentacja może być przydatna dla osób, które odczytały kod w przyszłości. –

5

O ile mi wiadomo, masz absolutną rację. Nie ma potrzeby sprawdzania, czy nie jest to null.

5

Może być operatorem == dla DocumentRepository zostanie nadpisany.

+0

Jak na świecie zastępujesz operatora? – Cody

+3

@DoctorOreo, http://msdn.microsoft.com/en-us/library/aa288467(v=vs.71).aspx –

+1

@DoctorOreo Na przykład zobacz moją odpowiedź w tym wątku: http: // stackoverflow. com/a/16655690/106159 To pokazuje, jak przeciążyć ==, ale pokazuje również, dlaczego musisz także zastąpić 'Equals()', jeśli to zrobisz. :) –

12

Odpowiedź Henk jest oczywista; Chciałem tylko dodać, że podejrzewam, skąd się to wzięło. Założę się, że w pewnym momencie w przeszłości ktoś napisał:

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

Oznacza to, że nieruchomość jest inicjowany od jego pierwszego użycia. Później ktoś zorientował się, że było to niepoprawne lub niepotrzebne, czy coś w tym stylu, i przepisał kod, aby umieścić konstrukcję w konstruktorze, czyniąc właściwość "chętną", a nie "leniwą", ale zapominając o wyzerowaniu.

Jeśli ludzie programują przez wycięcie i wklejenie z istniejącego kodu do nowego kodu, wzorzec może się rozprzestrzeniać. I wkrótce pojawia się mit, że tak trzeba to zrobić. Dlatego, jak podejrzewam, wielu programistów Visual Basic ma fałszywe przekonanie, że po zakończeniu pracy z Foo musisz powiedzieć Set Foo = Nothing; było to konieczne raz w niektórych scenariuszach, a teraz ludzie robią to nawet wtedy, gdy nie jest to konieczne, ponieważ tak właśnie robimy to tutaj.

Nawiasem mówiąc, prawdopodobnie chcesz powiedzieć:

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

Wydaje się mało prawdopodobne, że chcesz, aby użytkownicy Twojego serwisu, aby móc zmienić repozytorium w locie.

+0

Naprawdę doceniam to, że ktoś o dużej wiedzy i wpływach rozpoznał tę praktykę i napisał o niej. To wielkie źródło irytacji, gdy pytam kolegę "dlaczego to zrobiłeś", a odpowiedź brzmi: "Nie wiem, skopiowałem to skądś". – JDB

+9

@ Cyborgx37: * Dlaczego zawsze ustawiasz odniesienia do wartości zerowej? * Ponieważ utrzymuje aligatory z dala. * Ale w Seattle nie ma żadnych aligatorów. * Zobacz, jak dobrze działa? –

+2

Oczywiście, od czasu do czasu, gdy wychodzę z linii, aby usunąć taki głupiutki puch, "[dane listy płac zostaną usunięte] (http://dilbert.com/strips/comic/2009-08-30/)" . – JDB

1

Jest jeden scenariusz, który mogę wymyślić, gdzie to prawie ma sens - ale musiałbyś pracować w środowisku, w którym C# nie jest jedynym językiem (lub robisz dziwne kroki po kompilacji, takie jak jak to możliwe z Postsharp).

W języku C# lub VB.Net nie można kontrolować, kiedy wywoływany jest konstruktor klasy podstawowej, i nie ma żadnej składni umożliwiającej ustawienie podstawowych elementów klasy przed wywołaniem konstruktora klasy podstawowej - ale nic nie można temu zapobiec. w IL.

Jeśli pracowali w takim środowisku, a konstruktor masz pokazane faktycznie nic nie robi dalej z DocumentRepository, to może być ustawienie rzeczy poprawnie z następujących klas:

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

I wouldn” Ale chcę pracować w takim miejscu pracy.

Oto IL dla klasy:

.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 
    } 

} 
+0

Własne Umowy Kodowe Microsoft przesuwają kontrole umowy przed wezwaniem do konstruktora bazy, IIRC. (Używa binarnego przepisującego, ale myślę, że jest to przypadek, w którym krok po kompilacji nie jest dziwny.) I nie sprawdza, czy kontrakty mają efekty uboczne. – hvd

Powiązane problemy