2009-04-24 4 views
0

Plattform: Visual Studio 2008 SP1 mit ReSharper 4.1, .NET 3.5Ist mein Ansatz zum Lazy Loading fehlerhaft?

Ich habe eine Klasse mit einer statischen Methode, GetProperty<T>, die gemächlich einen Eigenschaftswert zurückgibt.

private static T GetProperty<T>(T backingField, Func<T> factory) 
    where T : class 
{ 
    if (backingField == null) 
     backingField = factory(); 
    return backingField; 
} 

Aber wenn ich oben Methode verwenden, um eine Eigenschaft zurück, ich zwei Warnungen bin immer, der sagt, dass private Träger Felder sind nicht belegt. Aber sie werden später nur zugewiesen, wenn sie benötigt werden.

alt text

Ist diese Warnung ignorable?
- Oder -
Ist meine Berufung, eine Eigenschaft fehlerhaft zu laden?

+0

Ich glaube, Sie würden die gleiche Warnung mit FXCop bekommen. –

Antwort

13

Ihre Methode ist fehlerhaft. Um diesen Ansatz zu verwenden, müssen Sie backingField einen ref Parameter eingeben.

private static T GetProperty<T>(ref T backingField, Func<T> factory) 

Dann auf GetProperty, ref _ImagXpress oder ref _PdfXpress passieren.

Die Art, wie Sie es tun, weist dem Parameter nur einen neuen Wert zu, nicht dem eigentlichen Hintergrundfeld.

+0

+1, obwohl ref Variablen sind in der Regel ein Code-Geruch. Da es privat ist, ist zumindest die Refugilität vollständig gekapselt. –

+0

Was ich nicht bekomme, wenn ich eine Klassenvariable '_PdfXpress' an' GetProperty' übergebe, sogar dachte, dass ich durch Wert, passiere, da '_PdfXpress' ein Referenztyp ist, sollte' _PdfXpress' nicht sein sollte in 'GetProperty' erstellt? Warum sollte ich explizit "ref" sagen? – Sung

+0

Einverstanden; nicht der Ansatz, den ich auch nehmen würde, aber es passt zu seinem Modell. –

5

Ihr Ansatz ist fehlerhaft. Deine Felder werden nie auf etwas gesetzt. das backingField Argument wird in der GetProperty<T> Methode festgelegt, aber diese nicht aktualisiert das Feld, das Sie nebenbei sind Sie werden in diesem Argument mit dem ref Schlüsselwort es so an geben wollen.

private static T GetProperty<T>(ref T backingField, Func<T> factory) 
1

Um eine andere Lösung vorzuschlagen:

Ich würde sagen, dass Sie nicht viel sparen, indem Sie diese Logik in eine separate Methode einkapseln. Sie erhöhen Ihre Komplexität ohne viel Gewinn. Ich würde vorschlagen, es so zu tun:

protected PdfXpress PdfXpress 
{ 
    get 
    { 
     if (_PdfXpress == null) 
      _PdfXpress = PdfXpressSupport.Create(); 

     return _PdfXpress; 
    } 
} 

protected ImagXpress ImagXpress 
{ 
    get 
    { 
     if (_ImagXpress == null) 
      _ImagXpress = IMagXpressSupport.Create(); 

     return _ImagXpress; 
    } 
} 

Sie fügen ein paar Zeilen, aber abnehmende Komplexität erheblich.

+0

Das war meine erste Implementierung; Aber ich würde mehr Eigenschaften hinzufügen müssen, also habe ich eine Refactoring-Methode zum Einkapseln von "Null" überprüft. – Sung

+0

Tanz: Es gibt so etwas wie Over-Engineering :) Ich würde diesen Ansatz sehr bevorzugen, da es viel klarer ist, was los ist. –

+0

@Adam: Ich beginne jetzt zu sehen, dass ich dazu tendiere, zu viel zu überarbeiten ... – Sung

3

Wie ich in Kommentaren zu einer anderen Antwort angegeben habe, ist die Notwendigkeit eines Ref-Parameters ein Code-Geruch. Vor allem, wenn Sie dies in einer Methode tun, brechen Sie das Prinzip der einfachen Verantwortung, aber noch wichtiger ist, dass der Code nur innerhalb Ihrer Erbhierarchie wiederverwendbar ist.

Es gibt hier ein Muster abgeleitet werden:

public class LazyInit<T> 
    where T : class 
{ 
    private readonly Func<T> _creationMethod; 
    private readonly object syncRoot; 
    private T _instance; 

    [DebuggerHidden] 
    private LazyInit() 
    { 
     syncRoot = new object(); 
    } 

    [DebuggerHidden] 
    public LazyInit(Func<T> creationMethod) 
     : this() 
    { 
     _creationMethod = creationMethod; 
    } 

    public T Instance 
    { 
     [DebuggerHidden] 
     get 
     { 
      lock (syncRoot) 
      { 
       if (_instance == null) 
        _instance = _creationMethod(); 
       return _instance; 
      } 
     } 
    } 

    public static LazyInit<T> Create<U>() where U : class, T, new() 
    { 
     return new LazyInit<T>(() => new U()); 
    } 

    [DebuggerHidden] 
    public static implicit operator LazyInit<T>(Func<T> function) 
    { 
     return new LazyInit<T>(function); 
    } 
} 

Dies ermöglicht es Ihnen, dies zu tun:

public class Foo 
{ 
    private readonly LazyInit<Bar> _bar1 = LazyInit<Bar>.Create<Bar>(); 
    private readonly LazyInit<Bar> _bar2 = new LazyInit<Bar>(() => new Bar("foo")); 

    public Bar Bar1 
    { 
     get { return _bar1.Instance; } 
    } 

    public Bar Bar2 
    { 
     get { return _bar2.Instance; } 
    } 
} 
+0

Scheint ein bisschen überentwickelt, sollte aber funktionieren und ist sicherlich eine kreative Lösung! –

+0

überentwickelt für diese spezielle Lösung, aber es bietet eine thread-sichere (relativ) faule Initialisierungsmethode, die überall wiederverwendet werden kann. Der von mir verwendete Produktionscode ist das Ergebnis mehrerer Iterationen des Refactorings von Lazy Initialization Code. –

+0

@Michael Meadows: Wow, ich denke, ich könnte LazyInit in meinen Code integrieren. Vielen Dank für die Bereitstellung einer differenzierten Perspektive zur Lösung des Problems. Ich habe nicht darüber nachgedacht, eine völlig neue Klasse für Lazy Loading zu erstellen. – Sung