我为什么要使用IDisposable而不是在c#中使用?

时间:2013-09-02 07:05:10

标签: c# .net garbage-collection .net-4.5

今天,我想用文件执行操作,所以我想出了这段代码

    class Test1
    {
        Test1()
        {
            using (var fileStream = new FileStream("c:\\test.txt", FileMode.Open))
            {
                //just use this filestream in a using Statement and release it after use. 
            }
        }
    }

但是在代码审查时,我被要求实现IDisposable接口和Finalizer方法

    class Test : IDisposable
    {
        Test()
        {
            //using some un managed resources like files or database connections.
        }

        ~Test()
        {
            //since .NET garbage collector, does not call Dispose method, i call in Finalize method since .net garbage collector calls this
        }

        public void Dispose()
        {
            //release my files or database connections
        }
    }

但是,我的问题是我为什么要这样做?

虽然根据我不能证明我的方法是合理的,但当使用语句本身可以释放资源时,我们为什么要使用 IDisposable

这里有什么特别的优点或遗漏了吗?

6 个答案:

答案 0 :(得分:8)

在您的示例中使用语句

是正确的,因为您仅在方法的范围内使用资源。例如:

Test1()
{
    using (FileStream fs = new FileStream("c:\\test.txt", FileMode.Open))
    {
        byte[] bufer = new byte[256];
        fs.Read(bufer, 0, 256);
    }
}

但是如果资源是在一个方法之外使用的,那么你应该创建Dispose方法。 这段代码错了:

class Test1
{
    FileStream fs;
    Test1()
    {
        using (var fileStream = new FileStream("c:\\test.txt", FileMode.Open))
        {
            fs = fileStream;
        }
    }

    public SomeMethod()
    {
        byte[] bufer = new byte[256];
        fs.Read(bufer, 0, 256);
    }
}

要做的事情是实现IDisposable以确保文件在使用后将被释放。

class Test1 : IDisposable
{
    FileStream fs;
    Test1()
    {
        fs = new FileStream("c:\\test.txt", FileMode.Open);
    }

    public SomeMethod()
    {
        byte[] bufer = new byte[256];
        fs.Read(bufer, 0, 256);
    }

    public void Dispose()
    {
        if(fs != null)
        {
            fs.Dispose();
            fs = null;
        }
    }
}

答案 1 :(得分:8)

首先要注意一点,因为您似乎对usingIDisposable之间的互动方式感到有些困惑:您之所以能说出using (FileStream fileStream = Whatever()) { ... },正是因为FileStream类实现IDisposable。您的同事建议您在 课程上实施IDisposable,以便您能够说using (Test test = new Test()) { ... }

对于它的价值,我认为你最初编写代码的方式比建议的更改更强烈,除非有一些令人信服的理由说明为什么你可能希望FileStream在{的整个生命周期内保持开放状态Test1 {1}}实例。可能出现这种情况的一个原因是,在Test1的构造函数被调用之后,该文件可能会从其他来源发生变化,在这种情况下,您将被困在较旧的数据副本。保持FileStream打开的另一个原因可能是,如果您特意想要在Test1对象处于活动状态时将文件锁定从其他位置写入。

通常,最好尽快释放资源,原始代码似乎就是这样做的。我有点怀疑的一件事是,工作是在你的构造函数中完成的,而不是在一些从外部显式调用的方法(解释:http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/)。但这完全是另一回事,并且与是否要使您的班级实施IDisposable的问题无关。

答案 2 :(得分:5)

根据您提供的信息,绝对没有理由在IDisposable上实施Test或终结器。

仅实现终结器以释放非托管资源(窗口句柄,GDI句柄,文件句柄)。除非您正在使用Win32 API或其他东西,否则通常不必执行此操作。 Microsoft已在FileStream中为您提供了这样的内容,因此您无需担心文件句柄。

终结器用于在对象被垃圾回收时清理非托管资源。

由于垃圾收集器在决定收集对象之前可能需要很长时间,因此您可能希望有一种方法来触发清理。不,GC.Collect() 是正确的方法。 ;)

要允许尽早清理本机资源而不必等待垃圾收集器,您可以在类上实现IDisposable。这使调用者可以在不等待GC的情况下触发清理。这样做会导致您的对象被GC释放。所有这一切都是免费的原生资源。

如果一个对象拥有另一个Disposable对象,那么拥有对象也应该实现IDisposable并简单地调用另一个对象的Dispose()

示例:

class Apple : IDisposable
{
    HWND Core;

    ~Apple() { Free(); }
    Free()
    {
        if(Core != null)
        {
            CloseHandle(Core); 
            Core = null;
        }
    }
    Dispose() { Free(); }
}

class Tree : IDisposable
{
    List<Apple> Apples;
    Dispose()
    {
        foreach(var apple in Apples)
            apple.Dispose();
    }
}

请注意Tree没有终结器。它实现了Dispose,因为它必须关心Apple清理。 Apple有一个终结器,可以确保它清理Core资源。 Apple允许通过调用Dispose()

进行早期清理

您不需要Dispose并且当然不需要终结器的原因是因为您的班级Test不拥有任何不受管理的成员字段或IDisposable。你碰巧创建了一个FileStream,这是一次性的,但你在离开方法之前要清理它。它不归Test对象所有。

此案例有一个例外。如果您正在编写一个您知道将由其他人继承的课程,而其他人可能必须实施IDisposable,那么您应该继续实施IDisposable。否则,调用者将不知道处置该对象(或甚至能够在没有强制转换的情况下)。然而,这是一种代码味道。通常,您不会从类继承并向其添加IDisposable。如果你这样做,那可能是糟糕的设计。

答案 3 :(得分:4)

“No One”给出的答案是正确的,using块只能用于实现IDisposable接口的类,并且对它的解释是完美的。你这边的问题是“为什么我需要在Test class上添加IDisposable,但在代码审查时,我被要求在Test class上实现IDisposable接口和Finalizer方法。”
答案很简单 1)按照许多开发人员遵循的编码标准,在使用某些资源的类上实现IDisposable总是好的,一旦该对象的范围超过该类中的Dispose方法将确保所有资源已经释放 2)已编写的类永远不会在将来不会进行任何更改,如果进行了此类更改并添加了新资源,则开发人员知道他必须在Dispose函数中释放这些资源。

答案 4 :(得分:2)

  

我们为什么要使用IDisposable

简短的回答是,任何未实现IDisposable的类都不能用于使用。

  

使用语句时本身可以释放资源

不,它本身不能释放资源。

正如我上面所写,你需要实现IDisposable以便能够使用。现在,当您实现IDisposable时,您将获得Dispose方法。在这种方法中,您可以编写所有代码,这些代码应该处理在不再需要该对象时需要处理掉的所有资源。

USING的目的是当一个对象超出其范围时,它将调用dispose方法,就是这样。

实施例

 using(SomeClass c = new SomeClass())
 { }

将转换为

 try
 {
     SomeClass c = new SomeClass();
 }
 finally
 {
     c.Dispose();
 }

答案 5 :(得分:0)

我认为问题更像是“我应该立即处理文件还是使用访问该文件的类的Dispose方法?”

这取决于:如果您在我看来只在构造函数中访问该文件,则没有理由实现IDisposable。使用是正确的方式

否则,如果您在其他方法中也使用相同的文件,也许这是一个很好的做法,打开文件一次,并确保在Dispose方法中关闭它(实现IDisposable)