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

now*_*ed. 4 .net c# 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. 
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

但在代码审查中,我被要求实现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
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

任何特定的优点或我在这里缺少一些东西?

Dmi*_*oly 8

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

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

但是如果资源是在一个方法之外使用的,那么你应该创建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);
    }
}
Run Code Online (Sandbox Code Playgroud)

要做的事情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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Mag*_*ken 8

一张小纸条第一,因为你似乎有点困惑如何usingIDisposable彼此交互:你为什么能说的原因using (FileStream fileStream = Whatever()) { ... }是,正是因为FileStream类实现IDisposable.你的同事曾建议是,你实现IDisposable你的类,这样你就可以说using (Test test = new Test()) { ... }.

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

通常,最好尽快释放资源,这是您的原始代码所做的.我有点怀疑的一件事是,工作是在你的构造函数中完成的,而不是在某些从外部明确调用的方法中完成(解释:http://misko.hevery.com/code-reviewers-guide/flaw-构造函数 - 实际工作/).但这完全是一个不同的问题,并且与是否使你的类实现的问题无关IDisposable.


dbw*_*dbw 5

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

  • 从技术上讲,这是一个答案,但是“未来的变化”是一个可怕的原因。除非您有特定的功能要确保可以实现,否则除非您有必要,否则没有理由使类实现IDisposposable。即使这样,您也必须处于无法更改类的接口但更改实现不会破坏更改的情况。 (4认同)
  • 如果使用IDisposable接口,将使用您的类的人将必须假定它具有不受管的资源。然后,他们将必须在“使用”语句(或调用dispose方法)中封装您的类的任何对象,以确保其代码不会泄漏非托管资源。如果每个人都这样做,则无论是否使用非托管资源,所有类的实例化都必须包含在“ using”语句中。对班级用户撒谎,并告诉他们班级使用非托管资源,真的是个好主意吗? (4认同)

dss*_*539 5

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

仅实现终结器以释放非托管资源(窗口句柄,GDI句柄,文件句柄).除非您正在使用Win32 API或其他东西,否则通常不必执行此操作.微软已经为您准备了这个,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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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