2010-03-22 2 views
21

Ich habe eine übergeordnete und untergeordnete Klasse, die beide IDisposable implementieren müssen. Wohin sollen virtual (und base.Dispose()?) Anrufe ins Spiel kommen? Wenn ich gerade den Dispose(bool disposing) Anruf außer Kraft setze, fühlt es sich wirklich merkwürdig an, dass ich IDisposable ohne ausdrückliche Dispose() Funktion einführe (nur die geerbte verwendend), aber alles andere.Implementierung von IDisposable für eine Unterklasse, wenn das übergeordnete Element auch IDisposable implementiert

Was ich getan hatte (trivialisiert ziemlich viel):

internal class FooBase : IDisposable 
{ 
    Socket baseSocket; 

    private void SendNormalShutdown() { } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    private bool _disposed = false; 
    protected virtual void Dispose(bool disposing) 
    { 
     if (!_disposed) 
     { 
      if (disposing) 
      { 
       SendNormalShutdown(); 
      } 
      baseSocket.Close(); 
     } 
    } 

    ~FooBase() 
    { 
     Dispose(false); 
    } 
} 

internal class Foo : FooBase, IDisposable 
{ 
    Socket extraSocket; 

    private bool _disposed = false; 
    protected override void Dispose(bool disposing) 
    { 
     if (!_disposed) 
     { 
      extraSocket.Close(); 
     } 
     base.Dispose(disposing); 
    } 

    ~Foo() 
    { 
     Dispose(false); 
    } 

} 
+0

[Msdn] (https://msdn.microsoft.com/en-us/library/system.idisposable (v = vs.110) .aspx0) hat Empfehlungen, wie Objekte zu Unterklasse IDisposable Implementierung . – PeterM

Antwort

23

Wenn ich Entsorgen nur die außer Kraft setzen (bool disposing) Anruf, es fühlt sich wirklich seltsam besagt, dass ich IDisposable implementieren, ohne eine explizite Entsorgen mit() Funktion (nur die geerbte verwenden), aber alles andere.

Das ist etwas, worüber Sie nicht besorgt sein sollten.

Wenn Sie eine IDisposable-Klasse unterklassifizieren, wird die gesamte "Dispose pattern" -Sanierung bereits von der Basisklasse für Sie gehandhabt. Sie sollen sich wirklich nichts tun, sondern die protected Dispose(bool) Methode überschreiben, und verfolgen, ob Sie bereits angeordnet worden sind (richtig ObjectDisposedException zu erhöhen.)

Einzelheiten meiner Blog-Post auf Subclassing from an IDisposable class sehen.


auch oft, ist es eine gute Idee, Einkapseln der IDisposable Klasse zu betrachten, anstatt sie zu Subklassen. Es gibt Zeiten, in denen die Unterklasse einer IDisposable-Klasse sinnvoll ist, aber sie sind etwas selten. Die Verkapselung ist oft eine bessere Alternative.

+0

Ah, ich denke, meine Sorgen waren unbegründet und ich war kurz davor, es richtig zu machen (ich frage mich, wo ich die Idee hatte, den Finalizer aus ... naja) wieder aufzunehmen. Und ja, Vererbung im Allgemeinen ist etwas, das ich immer weniger benutze. – Tanzelax

+0

Reed Ihr Blog ist brilliant ... aber beachten Sie die Anzahl Ihrer Antwort hier - 7 (1 ist meins:)) .... Menschen können nicht genug appreciate .. Schande. –

3

Die Idee dieses Muster ist, dass man die virtuelle Dispose Methode überschreiben, rufen base.Dispose, falls erforderlich. Die Basisklasse erledigt den Rest und ruft die virtuelle Dispose-Methode (und damit die korrekte Implementierung) auf. Die Unterklasse sollte auch nicht IDisposable implementieren müssen (es ist IDisposable über Vererbung)

+1

Ich nehme an, ich könnte einfach das explizite ': IDisposable' herausnehmen, da es diese Schnittstelle bereits erbt. Ich denke, ich mag es ausdrücklich zu sagen, dass "diese Klasse eine besondere Disposition hat, die passiert". Danke für die Eingabe. :) – Tanzelax

1

Die untergeordnete Klasse sollte die virtuelle Dispose außer Kraft setzen, eine spezifische Anordnung für die Unterklasse vornehmen und die Oberklasse 'Dispose' aufrufen, die wiederum ihre eigene Arbeit erledigt.

EDIT: http://davybrion.com/blog/2008/06/disposing-of-the-idisposable-implementation/ ist das Muster, das ich in solchen Fällen befolge. Nicht die "Disposable" -Klasse speziell, sondern die Vererbung und Außerkraftsetzung.

+0

Ich bin mir nicht sicher, ob mir die Idee gefällt, dem bereits aufgeblähten IDisposable-Muster noch mehr virtuelle/abstrakte Methoden hinzuzufügen ... – Tanzelax

+0

@Tanzelax Jedem das Eigene, nehme ich an. Der bereitgestellte Code war für mich nützlich, wo ich eine Hierarchie von Klassen habe, die wegwerfbar sein müssen. Der größte Teil des Setups befindet sich in der obersten Klassenklasse, und für jede Unterklasse wird minimaler zusätzlicher Code benötigt. –

3

Warum Dinge komplizieren, wenn Sie nicht brauchen?

Da Sie keine nicht verwalteten Ressourcen einkapseln, brauchen Sie nicht alles, was mit der Finalisierung zu tun hat. Und Ihre Klassen sind intern, was darauf hindeutet, dass Sie die Vererbungshierarchie in Ihrer eigenen Assembly steuern.

So würde der straighforward Ansatz:

internal class FooBase : IDisposable 
{ 
    Socket baseSocket; 

    private void SendNormalShutdown() 
    { 
    // ... 
    } 

    private bool _disposed = false; 

    public virtual void Dispose() 
    { 
    if (!_disposed) 
    { 
     SendNormalShutdown(); 
     baseSocket.Close(); 
     _disposed = true; 
    } 
    } 
} 

internal class Foo : FooBase 
{ 
    Socket extraSocket; 

    private bool _disposed = false; 

    public override void Dispose() 
    { 
    if (!_disposed) 
    { 
     extraSocket.Close(); 
     _disposed = true; 
    } 

    base.Dispose(); 
    } 
} 

Auch wenn Sie nicht verwalteten Ressourcen zu tun haben, würde ich sagen, Sie sind viel besser dran encapsulating them in ihrer eigenen Einweg-Klasse und mit ihnen wie würden Sie Verwenden Sie andere Einwegartikel; so geradlinig wie der obige Code.

+1

Können Sie sich einen Fall vorstellen, in dem eine Unterklasse einen Bereinigungsfinalizer haben sollte, wenn eine Elternklasse dies nicht tut (abgesehen von dem trivialen Fall, von Object zu erben)? Ich kann es verstehen, einen Finalizer hinzuzufügen, dessen Aufgabe es ist, sich über nicht ordnungsgemäß aufgegebene Objekte zu beschweren, aber IMHO sollte alles, was aufgeräumt werden muss, in seiner eigenen finalisierbaren Klasse sein; Ich kann an * NO * Ausnahmen denken. – supercat

+0

@supercat: Ich stimme völlig zu (http://codecrafter.blogspot.com/2010/01/revisiting-idisposable.html). –

0

Ich wende mich immer an Joe Duffys sehr eingehende Studie zu diesem Muster. Für mich ist seine Version Gospel.

http://joeduffyblog.com/2005/04/08/dg-update-dispose-finalization-and-resource-management/

Das erste, was zu erinnern ist, dass ein Finalizerthread nicht die meiste Zeit benötigt wird. Es dient zur Bereinigung nicht verwalteter Ressourcen, in denen Sie direkt native Ressourcen halten, d. H. Nur Ressourcen, die keinen eigenen Finalizer haben.

Hier ist ein Beispiel für ein Basisklasse-Unterklassenpaar.

// Base class 

    #region IDisposable Members 

    private bool _isDisposed; 

    public void Dispose() 
    { 
     this.Dispose(true); 
     // GC.SuppressFinalize(this); // Call after Dispose; only use if there is a finalizer. 
    } 

    protected virtual void Dispose(bool isDisposing) 
    { 
     if (!_isDisposed) 
     { 
      if (isDisposing) 
      { 
       // Clear down managed resources. 

       if (this.Database != null) 
        this.Database.Dispose(); 
      } 

      _isDisposed = true; 
     } 
    } 

    #endregion 


// Subclass 

    #region IDisposable Members 

    private bool _isDisposed; 

    protected override void Dispose(bool isDisposing) 
    { 
     if (!_isDisposed) 
     { 
      if (isDisposing) 
      { 
       // Clear down managed resources. 

       if (this.Resource != null) 
        this.Resource.Dispose(); 
      } 

      _isDisposed = true; 
     } 

     base.Dispose(isDisposing); 
    } 

    #endregion 

Beachten Sie, dass die Unterklasse ein eigenes Element _isDisposed hat. Notieren Sie auch die Ressourcen, da Sie keine Ausnahmen in diesen Blöcken wünschen.

Luke