5

Today, I wanted to perform an operation with a file so I came up with this code

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

But on code review, I was asked to implement IDisposable interface and Finalizer methods

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

But, my question is why should I ?

Although I cannot justify my methodology according to me, why should we use IDisposable when using statement can itself release resources)

Any specific advantages or am is missing something here?

4

6 回答 6

8

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

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;
        }
    }
}
于 2013-09-02T07:28:45.853 回答
8

首先要注意一点,因为您似乎对彼此之间的交互方式using和交互方式有些困惑:您之所以能够这么说,正是因为该类实现了. 您的同事建议您在课堂上实施这样您就可以说。IDisposableusing (FileStream fileStream = Whatever()) { ... }FileStreamIDisposableIDisposableusing (Test test = new Test()) { ... }

对于它的价值,我认为您最初编写代码的方式比建议的更改更可取,除非有一些令人信服的理由说明您可能希望在实例FileStream的整个生命周期内保持打开状态。Test1出现这种情况的一个原因是,在调用的构造函数Test1,文件可能会从其他来源发生更改,在这种情况下,您将被数据的旧副本卡住。保持打开状态的另一个原因FileStream可能是您特别想在对象处于活动状态时锁定文件以防止从其他地方写入Test1

一般来说,尽快释放资源是一种很好的做法,您的原始代码似乎就是这样做的。我有点怀疑的一件事是,工作是在您的构造函数中完成的,而不是在从外部显式调用的某种方法中完成的(解释: http: //misko.hevery.com/code-reviewers-guide/flaw-构造函数做实际工作/)。但这完全是另一回事,与是否让你的类实现的问题无关IDisposable

于 2013-09-02T07:30:08.883 回答
5

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

于 2013-09-02T07:54:38.003 回答
5

根据您提供的信息,绝对没有理由IDisposableTest.

只实现一个终结器来释放非托管资源(窗口句柄、GDI 句柄、文件句柄)。除非您正在 PInvoking 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到它。如果你这样做,它可能是糟糕的设计。

于 2013-09-03T17:14:08.690 回答
2

我们为什么要使用 IDisposable

简短的回答是任何不实现 IDisposable 的类都不能与 using 一起使用。

当 using 语句本身可以释放资源

不,它不能自己释放资源。

正如我在上面所写,您需要实现 IDisposable 才能使用 using。现在,当您实现 IDisposable 时,您将获得一个 Dispose 方法。在这种方法中,您编写的所有代码都应该处理不再需要对象时需要处理的所有资源。

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

例子

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

将转化为

 try
 {
     SomeClass c = new SomeClass();
 }
 finally
 {
     c.Dispose();
 }
于 2013-09-02T07:09:42.517 回答
0

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

这取决于:如果您仅在构造函数中访问文件,我认为没有理由实现 IDisposable。使用是正确的方法

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

于 2013-09-02T07:30:32.140 回答