C # calling IDisposable.Dispose () versus creating an object null
Consider the following code:
and. Toy class
class Toy
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private ToyAddon addOn;
public ToyAddon AddOn
{
get { return addOn; }
set { addOn = value; }
}
public void RemoveAddons()
{
/*
* someone said: this could lead to possible memory leak
* if this class implements IDisposable,
* then we are not disposing any of the resource
* */
//this.addOn = null;
/* instead use this */
var disposableToyAddOn = this.addOn as IDisposable;
if (disposableToyAddOn != null)
disposableToyAddOn.Dispose();
this.addOn = null;
}
public override string ToString()
{
return String.Format("{0}, {1}",
this.name,
(this.AddOn == null) ? "" : addOn.AddOnName);
}
}
B. Toy Addon
class ToyAddon
{
private string addOnName;
public string AddOnName
{
get { return addOnName; }
set { addOnName = value; }
}
}
C. Main program
class Program
{
static void Main(string[] args)
{
ToyAddon tAdd = new ToyAddon();
tAdd.AddOnName = "Amazing AddOn";
Toy t = new Toy();
t.Name = "Amazing Toy";
t.AddOn = tAdd;
t.RemoveAddons();
Console.WriteLine(t.ToString());
}
}
Now I was asked to check if the "having-a" object implements IDisposable, then call the dispose method. (please see the comments in the Toy class)
IMHO, if I make the reference null, then the object on the heap will be marked for Collection by GC.
It would be helpful if this can be done, about what happens on the heap and stack, and about the GC's role in what if a class (like ToyAddOn) implements IDisposable vs if it doesn't.
source to share
Now I was asked to check if the "have-a" object is implementing IDisposable, then call the dispose method.
IDisposable
is a pattern that is used to release unmanaged resources that must be explicitly released as the GC is unaware of them. Typically, the class that holds the unmanaged resources also implements a finalizer, and for that, it actually extends the lifespan of the object more than necessary. Dispose
in such situations, it usually calls a call GC.SupressFinalize
to remove the specified object from the finalization queue.
This is usually a good pattern if you have an object IDisposable
to implement IDisposable
yourself to make sure the underlying resource is disposed of.
IMHO if I make the reference null then the object on the heap will be marked for Collection by GC.
If the member you want to "exclude" is not a member static
, then (usually) there is no reason to do so. The compiler is smart enough to optimize it and the GC is smart enough to know that there is no more reference to the variable and clear it up.
Edit:
As @ScottChamberlain points out, there are times when a one-off object is still holding a reference and hence the GC does not consider it a GC. When you remove it and zero it out, you are hinting to the GC that it is "ready to collect".
source to share
Unless you manually dispose of the class, the unmanaged resources (if any) it is holding will only be cleaned up when the GC detects that there is enough memory pressure to warrant garbage collection and the object is GCed and Finalized.
Not all things that need to be scrapped add memory pressure, they can cling to things like Window Handles.
If the memory pressure never gets high enough, you probably never garbage collect these values โโbefore you run out of the resource it is throwing out. You see this a lot when people put Bitmap in a tight loop but don't use it, each bitmap uses a GDI + descriptor and doesn't account for managed memory pressure and doesn't trigger GC. This often causes people to get OutOfMemoryExceptions when they still have a lot of memory, because the real thing they ended up with was GDI + processing.
By explicitly disposing of your resources, you don't have to "hope you're lucky" and your object will be completed before you run out of unmanaged resources that it was holding.
The stack and the heap have nothing to do with this.
source to share
A sample try-casting something IDisposable
and getting rid of it if it implements that interface is very indicative of a design flaw. If the reference code type does not implement IDisposable
, this is a very strong indication that:
-
Owners of this type of link should not control it; even if derived types do implement
Disposable
, it should be the responsibility of the code that instantiated those types (and contains references to those types) to clean them up. -
The base type must implement
IDisposable
(even if 99% of the implementations do nothing) because some instances will have a lifetime outside the control of anyone who knows if they really need to be cleaned up. A prime example of these latter situations isIEnumerator<T>
. AlthoughIEnumerator
omittedIDisposable
, the need for it became apparent shortly after graduation; when Microsoft addedIEnumerator<T>
, they includedIDisposable
directly. While most implementationsIEnumerator<T>
can be eliminated without cleanup, there is no way that the code callingGetEnumerator
on the collection has no way of knowing if the implementationIEnumerator<T>
might be one of the rare ones that need to be cleaned up, and there is no practical way whenIEnumerable<T>
, the way thatGetEnumerator
createsIEnumerator<T>
, can be known when the client will be executed with it, so no one but the client canDispose
IEnumerator<T>
.
Determine if all the instances ToyAddOn
that implement can Disposable
end their lives while the only references are kept in code that has no idea if they need cleanup. If so, do ToyAddOn
implement Disposable
. If not, leave the cleanup for code that knows it's necessary.
source to share
I agree with these three answers. To simplify the answer to your question:
/ * * someone said: this could lead to a possible memory leak * if this class implements IDisposable, * then we do not dispose of any of the resources * * / //this.addOn = null;
What someone said doesn't really apply to your example. But this is absolutely correct, in general.
Suppose the Toy class lives much longer than ToyAddon. The ToyAddon class subscribes to events in the Toy Class. If so, ToyAddon must unsubscribe in the Dispose method. If you have not called the ToyAddon Dispose method after removing it from the Toy instance, the ToyAddon instace will live in memory as long as the Toy instance is alive.
And even if you call Dispose in the above case, there is still a reference to ToyAddon via 'addOn'. Therefore, in order to get rid of it completely, you need to set addOn to null.
IMHO if I make the reference null then the object on the heap will be marked for Collection by GC.
Right in your case, wrong, as I explained above.
source to share