ant2009
ant2009

Reputation: 22486

Is this correct for disposing an object using IDisposable

I have a class that implements the IDisposable interface. I am using a webclient to download some data using the AsyncDownloadString.

I am wondering have I correctly declared my event handlers in the constructor and within the using statement of the web client? And is this correct way to remove the event handlers in the Dispose method?

Overrule is this the correct way to use the IDisposable interface?

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;
        }
    }
}

Upvotes: 1

Views: 7746

Answers (4)

Joel Coehoorn
Joel Coehoorn

Reputation: 415600

There are two correct ways to use an IDisposable object:

  1. put it in a using block
    OR
  2. Wrap it in a class that also correctly implements IDisposable and dispose it when this new class is disposed. You now expect that all instances of your new class are created with using blocks.

Note that I said "or", not "and". Do one or the other, but not both.

Here, when you create your WebClient instance with a using block in the constructor, you dispose it before you ever have a chance to use it anywhere else. You should do just option two, in this case.

Upvotes: 8

Ben Robbins
Ben Robbins

Reputation: 2949

The other answers are correct but they have all missed the fact that you are declaring a finalizer when you shouldn't.

From the .Net Framework Design Guidelines (page 258):

  • Avoid making types finalizable.
  • Do make a type finalizable, if the type is responsible for releasing an unmanaged resources that does not have its own finalizer.

So the edited answer of rpetrich is correct, providing someone with edit privileges deletes the finalizer.

Upvotes: 3

Sayed Ibrahim Hashimi
Sayed Ibrahim Hashimi

Reputation: 44312

Since you have declared wc inside of a using statement you should not be used outside of that. So I would guess that your calls using wc inside of the GetBalance throw exceptions. You should remove the using block from the Balance contstructor.

See "using statement (C# Reference)" for more info on the using statement.

Upvotes: 3

rpetrich
rpetrich

Reputation: 32316

That is mostly correct except wc is being disposed twice and GetBalance will always be using wc after it's disposed!

Edit: a version with that correction:

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();
        }               
    }
}

Upvotes: 7

Related Questions