2013-05-22 17 views
14

Mit einem früheren Team mit denen ich gearbeitet, wann immer eine neuen Service-Klasse erstellt wurde, die Geschäftslogik zwischen der Datenschicht und Präsentationsschicht, etwa wie folgt wurde getan, um handhaben:Warum überprüfen Sie, ob eine Klassenvariable null ist, bevor Sie ein neues Objekt im Konstruktor instanziieren?

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

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

ich nie ganz verstanden, warum das Kontroll für null war da. Wenn der Konstruktor aufgerufen wird, bedeutet das, dass er NULL sein muss ... da es eine neue Instanz ist, richtig?

Warum sollte dies getan werden? Es scheint mir, als ob es ein überflüssiger Schritt ist, aber ich möchte nichts verpassen und es als schlechte Übung abtun.

+2

Ein Grund, warum ich denken konnte: irgendwo auf der Straße, können Sie es statisch und vergessen Sie es im Konstruktor und jetzt jedes Mal ein neues Objekt wird erstellt machen wollen , die statische Eigenschaft wird überschrieben. – Corak

+0

Der einzige Grund, warum ich für diese Überprüfung sehen konnte, ob es Methodenaufrufe oberhalb dieser Zeile im Konstruktor gibt, die das Repository festlegen könnten. (Ich weiß, dass es in Ihrem Beispiel nicht gibt) – Sayse

+0

Ist die 'DocumentRepository'-Eigenschaft eine Autoproperty genau so, wie Sie sie in Ihre Frage geschrieben haben? Wenn ja, dann ist es in der Tat sinnlos. Wenn nicht, dann können wir nicht antworten, ohne die tatsächliche Implementierung zu sehen. –

Antwort

17

In genau diesem Kontext: Ja, es ist überflüssig.

Es gibt keinen unmittelbaren Grund für diesen Code, könnte es ein übrig gebliebenes von einem älteren Verfahren oder Antizipation auf eine Implementierung mit mehreren Konstrukteuren sein. Aber ich würde nicht empfehlen, dieses "Muster" zu verwenden oder diesen Code zu behalten.

+3

@DoctorOreo: Ich würde vorschlagen, dass Sie, wenn Sie unnötiges Auschecken durchführen,' Debuggen ' .Assert (DocumentRepository == null); DocumentRepository = new DocumentRepository(); '- wenn der Check wirklich * in einem bizarren Szenario, das Sie nicht kennen, sinnvoll war, werden Sie bald darüber Bescheid wissen. –

+0

Die von @EricLippert vorgeschlagene Behauptung ist auch eine Möglichkeit für den Autor, seine Erwartungen an das Verhalten des Codes zu dokumentieren. Und diese Dokumentation kann für Leute, die den Code in Zukunft lesen, informativ sein. –

5

Soweit ich das beurteilen kann, sind Sie absolut richtig. Es ist nicht notwendig, nach null zu suchen.

5

Möge der Operator == für DocumentRepository ist überschrieben.

+0

Wie in der Welt überschreiben Sie einen Operator? – Cody

+3

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

+1

@DoctorOreo Ein Beispiel finden Sie in meiner Antwort in diesem Thread: http: // stackoverflow. com/a/16655690/106159 Das zeigt Ihnen, wie man == überlädt, aber es zeigt Ihnen auch, warum Sie auch 'Equals()' überschreiben müssen, wenn Sie dies tun. :) –

12

Henk Antwort ist natürlich richtig; Ich wollte nur hinzufügen, dass ich einen Verdacht habe, woher das kommt. Ich werde das in der Vergangenheit jemand irgendwann Wette schrieb:

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

Das heißt, die Eigenschaft auf seinem ersten Einsatz initialisiert. Später erkannte jemand, dass dies falsch oder unnötig war, und schrieb den Code neu, um die Konstruktion in den Konstruktor zu stellen, wodurch die Eigenschaft "eifrig" und nicht "faul" wurde, aber vergessen wurde, den Null-Check herauszunehmen.

Wenn die Menschen dann Programm durch das Ausschneiden und Einfügen von Code zu neuem Code existiert, kann das Muster verteilt. Und schon bald entsteht ein Mythos, dass es so gemacht werden muss. Dies ist, warum, so vermute ich, so viele Visual Basic-Programmierer den falschen Glauben, dass Sie Set Foo = Nothing sagen müssen, wenn Sie mit Foo fertig sind; es war einmal in irgendeinem Szenario notwendig, und jetzt machen Leute es auch, wenn es nicht notwendig ist, denn genau so machen wir es hier.

Übrigens, möchten Sie wahrscheinlich sagen:

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

Es scheint unwahrscheinlich, dass Sie die Benutzer des Service wollen im Fluge das Repository in der Lage sein zu ändern.

+0

Ich schätze wirklich, dass jemand mit sehr viel Wissen und Einfluss diese Praxis erkannt und darüber geschrieben hat. Es ist eine große Quelle der Irritation, wenn ich einen Kollegen frage "Warum hast du das gemacht?" Und die Antwort lautet: "Ich weiß es nicht, ich habe es von woanders kopiert." – JDB

+9

@ Cyborgx37: * Warum stellen Sie Ihre Referenzen immer auf null? * Weil es die Alligatoren fernhält. * Aber es gibt keine Alligatoren in Seattle. * Sehen Sie, wie gut es funktioniert? –

+2

Natürlich, hin und wieder, wenn ich aus der Reihe treten, um so dumme Flusen zu entfernen, "[die Gehaltsabrechnungsdaten werden gelöscht] (http://dilbert.com/strips/comic/2009-08-30/)" . – JDB

1

Es gibt ein Szenario, an das ich denken kann, wo dies fast Sinn macht - aber Sie müssten in einer Umgebung arbeiten, in der C# nicht die einzige Sprache ist (oder Sie machen merkwürdige Post-Compilierungsschritte, z wie es mit Postsharp möglich wäre).

In C# oder VB.Net können Sie nicht steuern, wenn der Basisklassenkonstruktor aufgerufen wird, und es gibt keine Syntax, mit der Sie Basisklassenmember festlegen können, bevor der Basisklassenkonstruktor aufgerufen wird - aber es gibt nichts zu verhindern in IL.

Wenn Sie wurden in einer solchen Umgebung zu arbeiten, und der Konstruktor Sie tatsächlich tut etwas gezeigt habe weiter mit DocumentRepository, dann könnten Sie die Dinge richtig für die folgende Klasse werden Einstellung:

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

I wouldn‘ Ich möchte jedoch an einem solchen Arbeitsplatz arbeiten.

Hier ist die IL für die Klasse:

.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

Microsoft eigene Code Contracts verschiebt die Vertragsprüfungen vor dem Aufruf an den Basiskonstruktor IIRC. (Es verwendet einen binären Rewriter, aber ich denke, das ist ein Fall, in dem der Post-Compilierungsschritt nicht komisch ist.) Und es prüft nicht, ob die Verträge Nebenwirkungen haben. – hvd

Verwandte Themen