tags:

views:

2729

answers:

4

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;
        }
    }
}
+3  A: 

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();
        }               
    }
}
rpetrich
Just for curiosity, would there be a problem with putting the:wc.DownloadProgressChanged -= new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged); wc.DownloadStringCompleted -= new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);inside the destructor, rather than the Dispose? (As these are not unmanaged objects)? Is the issue that the dispose may make the object invalid, so you need to do them here instead?
Zenox
`IDisposable` is a pattern for implementing deterministic disposal of resources. All resources should be cleaned up in the Dispose method--once it runs, the finalizer is suppressed. The only time the finalizer should run is if the object wasn't disposed properly. The main reason the event handlers are removed is so that we can be certain that `wc_DownloadStringCompleted` isn't received after the object is disposed; that would be unexpected.
rpetrich
Ben Robbins
+6  A: 

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.

Joel Coehoorn
+1  A: 

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.

Sayed Ibrahim Hashimi
+1  A: 

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.

Ben Robbins