Reputation: 2985
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
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
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:
Define disposable
inside a method of Foo
type Foo() =
member this.DoSomething() =
use disposable:IDisposable = // create an IDisposable
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
Reputation: 11577
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