Lay González
Lay González

Reputation: 2985

Will .Net call Dispose for me in this case?

For what I've read on the Internet, you should always call dispose on IDisposable objects as soon as you no longer need them.

This question is about a particular case where (for me) is obvious that objects should no longer be needed and perhaps .Net may call dispose for me.

Case in point:

type Foo() =
    let disposable:IDisposable = // create an IDisposable
    ...

The question is: As soon as foo of type Foo becomes unreachable (candidate for garbage collection; no references pointing to it), will disposable get disposed?

My guess is that disposable won't get disposed as soon as there are no references to foo, since there is no reference counting occurring in real time (AFAIK), but maybe when foo gets garbage collected .Net will dispose disposable.

For now, I'm avoiding to create disposables in that location, since it is not completely clear to me who should call dispose and even if .Net called dispose, I don't know how soon it would happen.

Upvotes: 2

Views: 882

Answers (3)

Mark Seemann
Mark Seemann

Reputation: 233150

Here's everything you'll need to know about IDisposable: CLR Inside Out: Digging into IDisposable

In short, Dispose will not be called by the Garbage Collector, but if the object has a finalizer, the finalizer will be called. All well-designed implementations of IDisposable that hold unmanaged resources will have a finalizer that does the same as the Dispose method does.

However, if you wait for the Garbage Collector to clear up your memory, you will get non-deterministic behaviour, and it's also likely that your application will use memory in a non-optimal way. Not disposing of disposable resources is likely to adversely affect performance.

However, in F#, it's often easy to ensure that objects are automatically disposed of: just use the use keyword instead of a let binding.

However, that doesn't work in this particular example because Foo is a class, which means that disposable isn't a value, but rather a field. That means it doesn't go out of scope until the object itself goes out of scope, but AFAIK, there's no specific language construct to support that (neither is there in C#).

Here's how you can implement the Dispose idiom for Foo:

open System

type Foo() =
    let disposable:IDisposable = // create an IDisposable
    abstract member Dispose : disposing : bool -> unit
    default this.Dispose disposing =
        if disposing then disposable.Dispose ()
    interface IDisposable with
        member this.Dispose () = 
            this.Dispose true
            GC.SuppressFinalize this

The only part that doesn't quite fit into the 'standard' Dispose idiom is that the virtual Dispose method (bool -> unit) isn't protected, because F# doesn't support that (AFAIK).

Upvotes: 2

Daws
Daws

Reputation: 702

As previously mentioned, Dispose will not be called directly by the Garbage Collector, although the finalizer will be called when disposable is garbage collected and will hopefully clean up its resources in a similar way.

Unfortunately, changing let to use does not solve your problem in this instance since it will yield you a compiler error:

type Foo() =    
    use disposable:IDisposable = // create an IDisposable

// error FS0523: 'use' bindings are not permitted in primary constructors

Your options here are:

  1. Define disposable inside a method of Foo

    type Foo() = 
        member this.DoSomething() =
            use disposable:IDisposable = // create an IDisposable
    
  2. Or, make Foo IDisposable and push the responsibility of disposing it to its consumers

    type Foo() =        
        let disposable:IDisposable = // create an IDisposable
    
        interface IDisposable with
            member this.Dispose() = disposable.Dispose()
    
        member this.DoSomething() = ()    
    
    let bar () =
        use t = new Foo()
        t.DoSomething()
    

Upvotes: 3

Obvious in this case perhaps but there's no call to .Dispose unless you ask for it (replace let with use for instance).

When the object is GC:ed (which it may never be, non-determinism and all that) if the object has a finalizer then the GC will add the object to the finalizer queue.

The finalizer thread then might run the finalizer on the object where the finalizer can free unmanaged resources but not managed resources like other .NET objects that implement IDisposable. In addition it's hard to free resources with thread-affinity, like Windows handles, in the finalizer as it's the wrong thread.

Finally the object may get GC:ed.

So the GC is great (kind of) for pure memory resources, for other resources we use deterministic and explicit resource collection by calling Dispose or use use.

Eric Lippert recently wrote an interesting blog on the wonderful world of finalizers which is worth checking out for more details.

Upvotes: 4

Related Questions