Why should I use IDisposable instead of using in c

2019-02-15 07:21发布

问题:

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?

回答1:

The answer given by "No One" is correct that using block can only be used for the classes that implement the IDisposable interface and explanation for it is perfect. The question from your side is "Why I need to add IDisposable on Test class and But on code review, I was asked to implement IDisposable interface and Finalizer methods on Test class."
The answer is simple
1) As the per the coding standards followed by so many developers it is always good to implement IDisposable on the classes which use some resources and once the scope of that object is over the Dispose method in that class will make sure all the resources have been released.
2) The class that has been written is never such that in future no changes will be made and if such changes are made and new resources are added then the developer knows that he has to release those resources in Dispose function.



回答2:

in your example using statement is right, because you use resources only in scope of your method. For example:

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

but if the resources are used outside of one method, then you should create Dispose method. This code is wrong:

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

The rigth thing to do is implement IDisposable to be sure, that the file will be released after it is used.

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


回答3:

A little note first, since you seem a bit confused about how using and IDisposable interact with each other: The reason why you're able to say using (FileStream fileStream = Whatever()) { ... } is precisely because the FileStream class implements IDisposable. What your coworkers have suggested is that you implement IDisposable on your class so that you'll be able to say using (Test test = new Test()) { ... }.

For what it's worth, I think the way you wrote the code initially is strongly preferable to the suggested change, unless there's some compelling reason why you might want to keep the FileStream open for the entire lifetime of a Test1 instance. One reason why this might be the case is that the file might be subject to change from some other source after the constructor of Test1 has been called, in which case you'll be stuck with an older copy of the data. Another reason for keeping the FileStream open could be if you specifically want to lock the file from being written to from elsewhere while a Test1 object is alive.

In general, it's good practice to release resources as soon as possible, which your original code seems to do. One thing I'm a little skeptical of is that the work is done in your constructor instead of in some method that's explicitly called from the outside (explanation: http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/). But this is a different matter altogether, and independent of the question of whether to make your class implement IDisposable.



回答4:

Based on the information you provided, there is absolutely no reason to implement IDisposable or a finalizer on Test.

Only implement a finalizer to release unmanaged resources (window handle, GDI handle, file handle). You usually do not have to ever do this unless you are PInvoking the Win32 API or something. Microsoft has kindly wrapped this for you in the FileStream so you do not have to worry about file handles.

A finalizer is meant to clean up unmanaged resources when an object is garbage collected.

Since the garbage collector might take a very long time before it decides to collect your object, you might want to have a way to trigger cleanup. No, GC.Collect() is not the right way to do that. ;)

To allow early cleanup of native resources without having to wait for garbage collector, you implement IDisposable on your class. This lets the caller trigger cleanup without waiting on the GC. This does not cause your object to be freed by the GC. All it does is free the native resource.

In the case where an object owns another object that is Disposable, then the owning object should also implement IDisposable and simply call the other object's Dispose().

Example:

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();
    }
}

Notice that Tree does not have a finalizer. It implements Dispose because it must care about Apple cleanup. Apple has a finalizer to make sure it cleans up the Core resource. Apple allows early cleanup by calling Dispose()

The reason that you do not need Dispose and certainly do not need a finalizer is because your class Test does not own any member field that is unmanaged or IDisposable. You do happen to create a FileStream, which is disposable, but you clean it up before leaving the method. It is not owned by the Test object.

There is one exception to this case. If you are writing a class that you know will be inherited by others, and the others may have to implement IDisposable, then you should go ahead and implement IDisposable. Otherwise, callers won't know to dispose the object (or even be able to without casting). However, this is a code smell. Usually, you would not inherit from a class and add IDisposable to it. If you do, it's probably poor design.



回答5:

why should we use IDisposable

Short answer is that any class that doesn't implement IDisposable cannot be used with using.

when using statement can itself release resources

no it cannot release resources itself.

As i wrote above that you need to implement IDisposable inorder to be able to use using. Now, when you implement IDisposable you get a Dispose method. In this method you write all the code that should take care of all the resources that needs to be disposed off when the object is no longer required.

The purpose of USING is that when an object goes out of its scope, it will call the dispose method and that is it.

Example

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

will translate to

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


回答6:

I think that the question is more like "Should I dispose the file immediately or with the Dispose method of the class that accesses to that file?"

It depends: if you access the file only in the constructor in my opinion there is no reason to implement the IDisposable. Using is the right way

Otherwise if you use the same file also in other methods, maybe it's a good practise open the file once and be sure to close it in your Dispose method (implementing the IDisposable)