Это правильно для удаления объекта с помощью IDisposable - PullRequest
1 голос
/ 18 мая 2009

У меня есть класс, который реализует интерфейс IDisposable. Я использую веб-клиента для загрузки некоторых данных, используя AsyncDownloadString.

Мне интересно, правильно ли я объявил свои обработчики событий в конструкторе и в операторе using веб-клиента? И это правильный способ удалить обработчики событий в методе Dispose?

Отменить это правильный способ использования интерфейса IDisposable?

public class Balance : IDisposable
{
    //Constructor
    WebClient wc;
    public Balance()
    {
        using (wc = new WebClient())
        {
            //Create event handler for the progress changed and download completed events
            wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
            wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);
        }
    }

    ~Balance()
    {
        this.Dispose(false);
    }

    //Get the current balance for the user that is logged in.
    //If the balance returned from the server is NULL display error to the user.
    //Null could occur if the DB has been stopped or the server is down.       
    public void GetBalance(string sipUsername)
    {
        //Remove the underscore ( _ ) from the username, as this is not needed to get the balance.
        sipUsername = sipUsername.Remove(0, 1);

        string strURL =
            string.Format("https://www.xxxxxxx.com", 
            sipUsername);

        //Download only when the webclient is not busy.
        if (!wc.IsBusy)
        { 
            // Download the current balance.
            wc.DownloadStringAsync(new Uri(strURL));             
        }
        else
        {
            Console.Write("Busy please try again");
        }
    }

    //return and display the balance after the download has fully completed
    void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        //Pass the result to the event handler
    }

    //Dispose of the balance object
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    //Remove the event handlers
    private bool isDisposed = false;
    private void Dispose(bool disposing)
    {
        if (!this.isDisposed)
        {
            if (disposing)
            {
                wc.DownloadProgressChanged -= new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
                wc.DownloadStringCompleted -= new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);

                wc.Dispose();
            }               
            isDisposed = true;
        }
    }
}

Ответы [ 4 ]

8 голосов
/ 18 мая 2009

Существует два правильных способа использования объекта IDisposable:

  1. положить его в using блок
    OR
  2. Оберните его в класс, который также правильно реализует IDisposable и избавьтесь от него, когда этот новый класс будет удален. Теперь вы ожидаете, что все экземпляры вашего нового класса создаются с помощью блоков using.

Обратите внимание, что я сказал "или", а не "и". Делайте одно или другое, но не оба.

Здесь, когда вы создаете свой экземпляр WebClient с блоком using в конструкторе, вы располагаете им до того, как у вас появится возможность использовать его где-либо еще. В этом случае вам следует сделать только второй вариант.

7 голосов
/ 18 мая 2009

Это в основном правильно, за исключением того, что wc утилизируется дважды, а GetBalance всегда будет использовать wc после утилизации!

Редактировать: версия с этим исправлением:

public class Balance : IDisposable
{
    //Constructor
    WebClient wc;
    public Balance()
    {
        wc = new WebClient();
        //Create event handler for the progress changed and download completed events
        try {
            wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
            wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);
        } catch {
            wc.Dispose();
            throw;
        }
    }

    ~Balance()
    {
        this.Dispose(false);
    }

    //Get the current balance for the user that is logged in.
    //If the balance returned from the server is NULL display error to the user.
    //Null could occur if the DB has been stopped or the server is down.       
    public void GetBalance(string sipUsername)
    {
        //Remove the underscore ( _ ) from the username, as this is not needed to get the balance.
        sipUsername = sipUsername.Remove(0, 1);

        string strURL =
            string.Format("https://www.xxxxxxx.com", 
            sipUsername);

        //Download only when the webclient is not busy.
        if (!wc.IsBusy)
        { 
            // Download the current balance.
            wc.DownloadStringAsync(new Uri(strURL));             
        }
        else
        {
            Console.Write("Busy please try again");
        }
    }

    //return and display the balance after the download has fully completed
    void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        //Pass the result to the event handler
    }

    private bool isDisposed = false;

    //Dispose of the balance object
    public void Dispose()
    {
        if (!isDisposed)
            Dispose(true);
        GC.SuppressFinalize(this);
    }

    //Remove the event handlers
    private void Dispose(bool disposing)
    {
        isDisposed = true;
        if (disposing)
        {
            wc.DownloadProgressChanged -= new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
            wc.DownloadStringCompleted -= new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);
            wc.Dispose();
        }               
    }
}
3 голосов
/ 06 апреля 2010

Другие ответы верны, но все они упустили тот факт, что вы объявляете финализатор, когда не должны.

Из Руководства по проектированию .Net Framework (стр. 258):

  • Избегайте создания типов финализируемых.
  • Do делает тип финализуемым, если тип отвечает за освобождение неуправляемых ресурсов, которые не имеют своего собственного финализатора.

Таким образом, отредактированный ответ rpetrich правильный, если кто-то с правами редактирования удалит финализатор.

3 голосов
/ 18 мая 2009

Поскольку вы объявили wc внутри оператора using, вы не должны использоваться вне этого. Так что я думаю, что ваши вызовы, использующие wc внутри GetBalance, генерируют исключения. Вы должны удалить блок using из контрагента Balance.

См. " using Statement (C # Reference) " для получения дополнительной информации об использовании Statement.

...