C# 使用IDisposable处理对象是否正确

C# 使用IDisposable处理对象是否正确,c#,idisposable,C#,Idisposable,我有一个实现IDisposable接口的类。我正在使用webclient使用AsyncDownloadString下载一些数据 我想知道我是否在构造函数和web客户端的using语句中正确声明了事件处理程序?这是删除Dispose方法中的事件处理程序的正确方法吗 否决这是使用IDisposable接口的正确方法吗 public class Balance : IDisposable { //Constructor WebClient wc; public Balance(

我有一个实现IDisposable接口的类。我正在使用webclient使用AsyncDownloadString下载一些数据

我想知道我是否在构造函数和web客户端的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;
        }
    }
}

这基本上是正确的,除了
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();
        }               
    }
}

这基本上是正确的,除了
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();
        }               
    }
}

有两种正确的方法可以使用IDisposable对象:

  • 使用
    块将其放入

  • 将它包装在一个类中,该类也正确实现IDisposable,并在释放这个新类时释放它。现在,您希望新类的所有实例都是使用
    块创建的
  • 请注意,我说的是“或”,而不是“和”。做一个或另一个,但不能同时做两个


    在这里,当您使用构造函数中的
    创建WebClient实例时,在您有机会在任何其他地方使用它之前,您会先处理它。在这种情况下,您应该只执行选项二。

    使用IDisposable对象有两种正确的方法:

  • 使用
    块将其放入

  • 将它包装在一个类中,该类也正确实现IDisposable,并在释放这个新类时释放它。现在,您希望新类的所有实例都是使用
    块创建的
  • 请注意,我说的是“或”,而不是“和”。做一个或另一个,但不能同时做两个


    在这里,当您使用构造函数中的
    创建WebClient实例时,在您有机会在任何其他地方使用它之前,您会先处理它。在这种情况下,您应该只执行选项二。

    因为您在using语句中声明了wc,所以不应该在using语句之外使用。所以我猜在GetBalance中使用wc的调用会抛出异常。 您应该从Balance Constructor中删除using块


    有关using语句的更多信息,请参见“”。

    由于您已在using语句内声明了wc,因此不应在using语句外使用wc。所以我猜在GetBalance中使用wc的调用会抛出异常。 您应该从Balance Constructor中删除using块


    有关using语句的更多信息,请参见“”。

    其他答案是正确的,但它们都忽略了一个事实,即您正在声明终结器,而您不应该这样做

    根据.Net Framework设计指南(第258页):

    • 避免使类型可终结
    • 如果类型负责释放没有自己终结器的非托管资源,则执行使类型可终结

    因此,rpetrich的已编辑答案是正确的,如果有人具有编辑权限,则会删除终结器。

    其他答案是正确的,但它们都忽略了一个事实,即您正在声明终结器,而您不应该声明终结器

    根据.Net Framework设计指南(第258页):

    • 避免使类型可终结
    • 如果类型负责释放没有自己终结器的非托管资源,则执行使类型可终结

    因此,rpetrich的编辑答案是正确的,如果有人具有编辑权限,则会删除终结器。

    出于好奇,放置:wc.DownloadProgressChanged-=new DownloadProgressChangedEventHandler(wc\u DownloadProgressChanged)会有问题吗;wc.DownloadStringCompleted-=新的DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);在析构函数中,而不是在Dispose中?(因为这些不是非托管对象)?dispose是否会使对象无效,因此您需要在此处执行这些操作?
    IDisposable
    是一种实现资源确定性处置的模式。应该清理Dispose方法中的所有资源——一旦它运行,终结器将被抑制。终结器应该运行的唯一时间是对象未正确处置的情况。删除事件处理程序的主要原因是,我们可以确定在处理对象后不会收到
    wc\u DownloadStringCompleted
    ;这是出乎意料的。Zenox,除非必须,否则永远不应该实现终结器-它只是用于清理非托管内存。您肯定不想引用终结器中的其他对象,因为不能保证它们尚未被释放/终结。请参阅下面我的答案&有关更长的答案,请掌握框架设计指南。基本上,您应该始终询问是否需要终结器,并非常仔细地检查它的功能。来自终结器的未处理异常会立即导致应用程序崩溃,无法恢复(它如何恢复,它在自己的线程中运行)。您应该删除终结器/析构函数。它不属于您,您也不需要它,因为内置在现有webclient类型中的终结器仍将运行并负责处理该对象。出于好奇,放置:wc.DownloadProgressChanged-=new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged)会有问题吗;wc.DownloadStringCompleted-=新的DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);在析构函数中,而不是在Dispose中?(因为这些不是非托管对象)?是指dispose可能使对象无效的问题,