C # как реализовать метод Dispose - PullRequest
30 голосов
/ 20 сентября 2011

Мне нужен совет по реализации метода Dispose.

В нашем приложении пользователь разрабатывает свой собственный пользовательский интерфейс.У меня есть окно предварительного просмотра, которое показывает, как будет выглядеть пользовательский интерфейс.Все объекты, нарисованные в этом пользовательском интерфейсе, в конечном итоге являются производными от общего базового класса ScreenObject.Мой менеджер предварительного просмотра содержит одну ссылку на объект ScreenGrid, который является объектом сетки для всей области предварительного просмотра.

Вопрос № 1

Некоторые из моих производных классов экрана содержатна неуправляемые ресурсы, такие как соединение с базой данных, растровое изображение и элемент управления WebBrowser.Эти классы должны избавляться от этих объектов.Я создал виртуальный метод Dispose в базовом ScreenObject базовом классе, а затем реализовал метод переопределения Dispose в каждом из производных классов, которые содержат неуправляемые ресурсы.Однако сейчас я только что создал метод с именем Dispose, я не реализую IDisposable.Должен ли я реализовать IDisposable?Если да, то как мне это реализовать?

  • Только на производных классах с неуправляемыми ресурсами
  • Базовый класс и производные классы с неуправляемыми ресурсами ИЛИ
  • Базовый класс и все производные классы, включая те, которые не имеют неуправляемых ресурсов

Неправильно ли помещать виртуальный Dispose метод в базовый класс, который не имеет неуправляемыхресурсы, чтобы вы могли воспользоваться преимуществами полиморфизма?

Вопрос № 2

Читая о методе Dispose и интерфейсе IDisposable, Microsoft заявляет, что утилизацияобъект должен вызывать только метод Dispose для своего родителя.Родитель вызовет его для своего родителя и так далее.Мне это кажется задом наперед.Я, возможно, захочу избавиться от ребенка, но держу его родителя рядом.

Я бы подумал, что должно быть наоборот, объект, которым нужно распоряжаться, должен распоряжаться своими детьми.Затем дети должны избавиться от своих детей и т. Д.

Я здесь не прав или я что-то упускаю?

Ответы [ 3 ]

35 голосов
/ 20 сентября 2011

Вопрос 1: Реализуйте также IDisposable, используя следующий шаблон:

public class MyClass : IDisposable
{
    bool disposed;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                //dispose managed resources
            }
        }
        //dispose unmanaged resources
        disposed = true;
    }

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

Вопрос 2: Что означает Microsoft, так это то, что вызовы производного класса располагаются в его родительском классе.Владелец экземпляра вызывает Dispose только для самого производного типа.

(сокращенный) пример:

class Parent : IDisposable 
{
    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                //dispose managed resources
            }
        }
        //dispose unmanaged resources
        disposed = true;
    }

}
class Child : Parent, IDisposable 
{ 
    protected override void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                //dispose managed resources
            }
            base.Dispose(disposing);
        }
        //dispose unmanaged resources
        disposed = true;
    }

}
class Owner:IDisposable
{
    Child child = new Child();
    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                if(child!=null)
                {
                    child.Dispose();
                }
            }
        }
        //dispose unmanaged ressources
        disposed = true;
    }
}

Владелец вызывает Dispose только для дочернего элемента, но не дляродитель.Ребенок отвечает за звонок Dispose на Родителя.

7 голосов
/ 13 декабря 2011

Вопрос 1:

В зависимости от типов объектов, которые вы перечисляете (например, База данных, Веб-браузер, Растровое изображение и т. Д.), Это только 1003 * управляемые ресурсы в отношении .NET. Таким образом, вы должны реализовать IDisposable для любого класса, который имеет одноразовые типы в качестве членов. Если они являются локально объявленными экземплярами, вы просто вызываете 'using ()' для них. Хотя в этих случаях, о которых вы упоминаете, есть неуправляемые ресурсы, но они отвлекаются от вас .NET через используемые вами типы. Поскольку вы используете только управляемые типы, вы должны реализовать IDisposable , но без финализатора . Вам нужно реализовать финализатор, только если у вас действительно неуправляемые ресурсы в качестве членов класса.

Вопрос 2:

Похоже, вы путаете наследование (is a) с агрегацией / сдерживанием (has a). Например, если «Контейнер» содержит одноразовый ресурс в качестве члена класса, он называется агрегацией / сдерживанием. Таким образом, вызов base.Dispose() в реализации IDisposable Контейнера не имеет ничего общего с удалением одноразового ресурса внутри Контейнера. Следует помнить, что если класс является производным от Container, скажем «DerivedContainer», то он является экземпляром контейнера, хотя и с дополнительными членами и / или функциональными возможностями. Таким образом, любой экземпляр DerivedContainer имеет все члены, которые имеет его базовый класс «Контейнер». Если вы никогда не вызывали base.Dispose(), одноразовый ресурс в «Контейнере» не был бы освобожден должным образом (на самом деле это было бы GC, но по многим причинам плохая практика - просто «позволять .NET позаботиться об этом») - пожалуйста, обратитесь на мой ответ на Разве это плохая практика - зависеть от автоматического сборщика мусора .NET? .

Если бы вы не вызывали базовый класс Dispose(), вы бы получили частично удаленный объект (расположенный в производном классе, но не в базовом классе) - очень плохой сценарий. Поэтому очень важно назвать базовый класс Dispose().

У меня есть шаблон наилучшей практики, который я разработал (с большим опытом и отладкой дампов памяти), написанный в моем блоге в качестве примера. В нем показано, как реализовать IDisposable для базового класса, а также для производного класса:

http://dave -black.blogspot.com / 2011/03 / как-делать-вы-правильно-implement.html

1 голос
/ 11 октября 2018

Реализую IDisposable

 class ConnectionConfiguration:IDisposable
{
    private static volatile IConnection _rbMqconnection;
    private static readonly object ConnectionLock = new object();
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    protected virtual void Dispose(bool disposing)
    {
        if (!disposing)
        {
            return;
        }
        if (_rbMqconnection == null)
        {
            return;
        }
        lock (ConnectionLock)
        {
            if (_rbMqconnection == null)
            {
                return;
            }
            _rbMqconnection?.Dispose();//double check
            _rbMqconnection = null;
        }
    }
}
...