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?

6 Answers6

9

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;
        }
    }
}
Dmitrii Dovgopolyi
  • 6,231
  • 2
  • 27
  • 44
8

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.

Magnus Grindal Bakken
  • 2,083
  • 1
  • 16
  • 22
5

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.

Dmitrii Dovgopolyi
  • 6,231
  • 2
  • 27
  • 44
Deepak Bhatia
  • 6,230
  • 2
  • 24
  • 58
  • 4
    Technically this is an answer, but "future changes" is a terrible reason. Unless you have specific features in mind that you are pretty sure will be implemented there is no reason to make a class implement IDisposable unless it needs to. Even then you have to be in a situation where you can not change the interface of the class but changing the implementation isn't going to be breaking change. – Joel McBeth Oct 25 '13 at 02:23
  • 4
    The person that will be using your class will have to assume that it has unmanaged resources if it uses the IDisposable interface. They will then have to enclose any object of your class in a "using" statement (or call the dispose method) to ensure that their code is not leaking unmanaged resources. If everyone did this, regardless of using unmanaged resources or not, all instantiations of classes would have to be enlosed in "using" statements. Is it really a good idea to lie to the users of your class and tell them that your class uses unmanaged resources, when it does not? – David Rector Mar 31 '14 at 21:01
  • @DavidRector my first point did mention `IDisposable on the classes which use some resources` .... – Deepak Bhatia Apr 01 '14 at 14:04
5

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.

dss539
  • 6,804
  • 2
  • 34
  • 64
  • 1
    Gotta disagree with your very last point there. (Everything before was spot-on.) There is absolutely no reason to implement IDisposable if your specific instance (or instances it manages) doesn't manage unmanaged resources. Consider a base Settings class that knows how to add and remove values, validate them, etc. But it has no knowledge of storage. Now you subclass that as FileSettings or DBSettings which each use unmanaged resources. They each should implement IDisposable (unless of course they use those resources in a using statement internally) even though the base class doesn't. – Mark A. Donohoe Dec 08 '15 at 20:19
  • (continuing...) Both of those subclasses should add IDisposable support but the base Settings class should not. Why not? Consider another resource called SomeOtherSettings that doesn't use managed resources. If you force the base class to implement IDisposable, you've now told SomeOtherSettings that they need to worry about disposing something, meaning they too now have to support IDisposable. You've just stunk up the code hierarchy. See what I mean? That to me is the real smell. – Mark A. Donohoe Dec 08 '15 at 20:22
  • @MarqueIV yes it stinks to add `IDisposable` but it is not really avoidable. This is due to the Liskov Substitution Principle. If you have a `Settings` base class, then any consumer who accepts objects of type `Settings` must also accept objects of type `FileSettings`. Now, if the `FileSettings` class requires cleanup, how is the consumer supposed to know this? This is why I mentioned type casting in that point. If `Settings` subclasses *may* implement `IDisposable`, then every consumer who owns a `Settings` object must always check if the object casts to `IDisposable`. – dss539 Dec 08 '15 at 22:02
  • @MarqueIV When you design the `Settings` base class, you have defined the contract of the class. Each method is a capability of the class. `IDisposable` is special in that it also communicates *responsibilities* of the calling code. It's risky to add in new responsibilities for the caller in subclasses, because then everyone who operates on `Settings` objects has to magically know about this possible responsibility required by some subclasses. It's safest just to add that responsibility into the contract of the base class, so avoid surprising consumers. – dss539 Dec 08 '15 at 22:04
  • 1
    I would argue that if disposing isn't part of the base class's responsibility, than anywhere that takes that base class *shouldn't* know about it because it's not part of that contract, perfectly following Liskov's principle. The creator of the subclass however should, and would because they would be creating an instance of FileSettings. That's why I kinda have to stay with my original statement it's more smelly to add than not. After all, how do I know who is going to subclass Settings and what they may add? Should I throw every possible thing in it? No. So why is IDisposable different? – Mark A. Donohoe Dec 08 '15 at 22:10
  • Continuing again... in order to cast to the base class from the subclass you have to have an instance of that subclass. That subclass instance is where IDisposable should be implemented because you're creating it, it needs it, and therefore you're responsible for it's cleanup (or at least telling someone else to do so.) Someone who only knows about Settings can't create an instance of FileSettings therefore they have no business knowing about it. You wouldn't blindly create something, then release it to the void, ignoring your responsibilities. THAT would be code smell. – Mark A. Donohoe Dec 08 '15 at 22:14
  • @MarqueIV You don't have to *create* the object to *use* the object. The point of the LSP is that *consumers* can treat every subclass the same as the base class. So you can have a `SettingsRepository` that contains a `List` field that contains all settings. Now you require `SettingsRepository` to either have a separate `List` to track those, or you have to attempt to cast each object to `IDisposable`. But what really will happen is that whoever writes `SettingsRepository` will assume that he can just use the base class as a contract. Disposal won't happen. – dss539 Dec 08 '15 at 22:30
  • Again, I have to disagree. What you are saying is true only if that repository is the owner and I would argue the creator is the owner. Having a reference to something doesn't mean you're the owner. And if the owner is to be transferred to the repository, you could let it know. Better yet, if the repository may handle disposables, it can check if the relevant object implements IDisposable without forcing all objects to implement it unnecessarily. – Mark A. Donohoe Dec 08 '15 at 23:06
  • You just suggested the thing that should be avoided. And no, creator != owner. I suppose we will have to agree to disagree. – dss539 Dec 09 '15 at 00:38
  • But you were the one that called out IDisposable as some 'magical' special case that should be implemented even if its not needed because a subclass *may* implement it. I'm saying if you go that 'special case' route (which again I disagree with in the first place) then have it be handled by the consumer. After all, in your suggestion, the consumer would know about it anyways so why not test for it and keep your objects clean? My take however is that consumer should never know about it anyway. Only the owner should know since it isn't relative to anyone else. But disagree we shall. – Mark A. Donohoe Dec 09 '15 at 03:11
2

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();
 }
Ehsan
  • 31,833
  • 6
  • 56
  • 65
0

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)

Emanuele
  • 56
  • 6