Frage

Ich habe eine abstrakte Klasse, die IDisposable implementiert, etwa so:

public abstract class ConnectionAccessor : IDisposable
{
    public abstract void Dispose();
}

In Visual Studio 2008 Team System, lief ich Code-Analyse auf mein Projekt und einer der Warnungen, die kam war die folgende:

  

Microsoft.Design: Ändern 'ConnectionAccessor.Dispose ()', so dass es Dispose (true) aufruft, ruft dann GC.SuppressFinalize auf der aktuelle Objektinstanz ( 'this' oder 'Me' in Visual Basic), und dann kehrt .

Ist es nur albern, mich zu sagen, den Körper einer abstrakten Methode zu ändern, oder soll ich etwas tun, weiter in irgendeiner abgeleiteten Instanz Dispose?

War es hilfreich?

Lösung

Sie sollten die herkömmlichen Muster für die Umsetzung Dispose folgen. Herstellung Dispose() virtuelle ist schlechte Praxis betrachtet, da das herkömmliche Muster Wiederverwendung von Code in „Managed Bereinigung“ (API-Client aufrufen Dispose() direkt oder über using) und „unmanaged Bereinigung“ (GC Aufruf Finalizerthread) betont. Zur Erinnerung, das Muster ist dies:

public class Base
{
    ~Base()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this); // so that Dispose(false) isn't called later
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
             // Dispose all owned managed objects
        }

        // Release unmanaged resources
    }
}

Schlüssel hier ist, dass es keine Überschneidungen zwischen Finalizerthread und Dispose für nicht verwaltete Bereinigung, und doch jede abgeleitete Klasse kann sowohl verwaltete und nicht verwaltete Bereinigung erweitern.

Für Ihren Fall, was Sie tun sollten, ist dies:

protected abstract void Dispose(bool disposing)

und lassen Sie alles andere als ist. Auch das ist von zweifelhaftem Wert, da Sie Ihre abgeleiteten Klassen sind die Durchsetzung Dispose jetzt zu implementieren - und wie Sie wissen, dass alle von ihnen es brauchen? Wenn Ihre Basisklasse hat nichts zu verfügen, aber die meisten abgeleiteten Klassen wahrscheinlich tun (mit wenigen Ausnahmen, vielleicht), dann bietet nur eine leere Implementierung. Es ist, was System.IO.Stream (selbst abstrakt) der Fall ist, so gibt es Präzedenzfall.

Andere Tipps

Die Warnung im Grunde sagt Ihnen die zu implementieren Entsorgen Muster in Ihrer Klasse.

Der resultierende Code soll wie folgt aussehen:

public abstract class ConnectionAccessor : IDisposable
{
    ~ConnectionAccessor()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
    }
}

Der einzige Nachteil, den ich mit den Antworten bisher zur Verfügung gestellt haben würde, ist, dass sie alle davon ausgehen, dass Sie Notwendigkeit einen Finalizer zu haben, was nicht unbedingt der Fall ist. Es ist eine ziemlich deutliche Performance-Overhead mit Abschluss verbunden ist, was ich nicht auf all meinen abgeleiteten Klassen aufzwingen will, wenn es nicht notwendig ist.

Siehe diesem Blog-Eintrag von Joe Duffy, was erklärt, wenn Sie einen Finalizer nicht brauchen können oder nicht, und wie richtig die Dispose-Muster in jedem Fall zu implementieren.
Zusammenfassend Joe Blog-Post, es sei denn, Sie etwas ziemlich Low-Level tun mit dem nicht verwalteten Speicher zu tun, sollten Sie einen Finalizer nicht implementieren. Als allgemeine Faustregel gilt, wenn Ihre Klasse enthält nur Verweise auf verwalteten Typen, die IDisposable selbst implementieren, müssen Sie die Finalizerthread nicht brauchen (aber sollte IDisposable implementieren und entsorgen diese Ressourcen). Wenn Sie nicht verwalteten Ressourcen aus dem Code direkt Zuteilung werden (PInvoke?) Und diese Ressourcen befreit werden müssen, benötigen Sie ein. Eine abgeleitete Klasse kann immer einen Finalizer hinzufügen, wenn sie es wirklich braucht, aber alle abgeleiteten Klassen zwingt einen Finalizer zu haben, indem sie in der Basisklasse setzen bewirkt, dass alle abgeleiteten Klassen durch die Leistungseinbußen von finalizable Objekte betroffen sein, wenn dieser Aufwand nicht sein kann erforderlich.

Während es ein wenig wie kleinlich scheint, ist der Rat gültig. Sie zeigt bereits, dass Sie alle Untertypen von ConnectionAccessor rechnen müssen etwas , dass sie entsorgen müssen. Daher ist es besser scheinen, um sicherzustellen, dass die richtige Bereinigung (in Bezug auf dem GC.SuppressFinalize Aufruf) von der Basisklasse durchgeführt wird, statt auf jedes Subtyp angewiesen, es zu tun.

Ich verwende das dispose Muster in Bruce Wagners Buch erwähnt Effective C # die im Grunde ist:

public class BaseClass : IDisposable
{
    private bool _disposed = false;
    ~BaseClass()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        _disposed = true;
    }
}

public void Derived : BaseClass
{
    private bool _disposed = false;

    protected override void Dispose(bool disposing)
    {
        if (_disposed) 
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        base.Dispose(disposing);
        _disposed = true;
    }

Die Warnung ist allerdings interessant. Eric Lippert, einer der C # Designer, gebloggt, warum Fehlermeldungen sein sollten „Diagnose aber nicht normativ: beschreibt das Problem, nicht die Lösung“. hier lesen .

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top