C# Actions and GC - c#

I am using Actions in C# and I was wondering if I need to set the instance of Action to null once I wish the GC to collect the objects properly? Here is an example:
public class A
{
public Action a;
}
public class B
{
public string str;
}
public class C
{
public void DoSomething()
{
A aClass = new A();
B bClass = new B();
aClass.a = () => { bClass.str = "Hello"; }
}
}
Inside my Main method I have something like this:
public void Main(...)
{
C cClass = new C();
cClass.DoSomething();
Console.WriteLine("At this point I dont need object A or B anymore so I would like the GC to collect them automatically.");
Console.WriteLine("Therefore I am giving GC time by letting my app sleep");
Thread.Sleep(3000000);
Console.WriteLine("The app was propably sleeping long enough for GC to have tried collecting objects at least once but I am not sure if A and B objects have really been collected");
}
}
Please read the Console.WriteLine text it will help you understand what I am asking here.
If I apply my understanding of GC to this example the GC would never collect the objects since A cannot be destroyed because it holds instance of B. Am I right?
How can I properly collect those two objects? Do I need to set instances of Actions to null just to let GC collect objects before the end of application or is there already some kind of very smart mechanism by GC that knows how to destroy objects who have Actions such as A and B are?
EDIT: The question is about GC and collecting objects properly. Its not about calling the method collect().

There are numerous problems with this question. Rather than answer your question directly I'm going to answer the questions you should be asking.
Let's first disabuse you of your notions about the GC.
Will sleeping for a long time activate the garbage collector?
No.
What activates the garbage collector?
For testing purposes you can use GC.Collect() and GC.WaitForPendingFinalizers(). Use these only for testing purposes; it is a bad practice to use them in production code except in some very rare circumstances.
Under normal situations the things that trigger a GC are complicated; the GC is a highly tuned piece of machinery.
What are the semantics of garbage collection insofar as closed-over outer variables are concerned?
The lifetime of a closed-over outer variable of a lambda that is converted to a delegate is extended to be not shorter than the lifetime of the delegate.
Suppose I have a variable of type Action which is initialized with a lambda that is closed over an outer local variable of reference type. In order to make the object referred to by that variable eligable for collection, do I have to set the variable of type Action to null?
In the vast majority of cases, no. The garbage collector is very smart; just let it do its work and do not worry about it. Eventually the runtime will determine that the Action variable cannot be reached by any live root and will make it eligable for collection; the closed-over outer variable will then become eligible.
There may be extremely rare situations in which you want to throw away references to the Action sooner, but they are rare; the vast majority of time, just let the GC do its job without interference.
Are there situations in which outer variables can have their lifetimes extended too long?
Yes. Consider:
void M()
{
Expensive e = new Expensive();
Cheap c = new Cheap();
Q.longLived = ()=>c; // static field
Q.shortLived = ()=>e; // static field
}
When M() is executed a closure is created for both delegates. Suppose that shortLived is going to be set to null soon, and longLived is set to null far in the future. Unfortunately both local variables have their lifetimes extended to the lifetime of the object referred to by longLived, even though only c is still reachable. The expensive resource e is not released until the reference in longLived is dead.
Numerous programming languages have this problem; some implementations of JavaScript, Visual Basic, and C# all have this problem. There is some talk of fixing it in the Roslyn release of C# / VB but I do not know if that will come to fruition.
In that case the solution is to avoid the situation in the first place; don't make two lambdas that share a closure if one of the delegates will live much longer than the other.
Under what circumstances does a local that is not a closed-over outer variable become eligable for collection?
The moment that the runtime can prove that a local cannot be read from again, the thing it references becomes eligable for collection (assuming the local is the only root of course.) In your example program there is no requirement that the references in aClass and bClass remain alive until the end of the method. In fact, there are rare but possible circumstances in which the GC can be deallocating an object on one thread while it is still in its constructor on another thread! The GC can be very aggressive about determining what is dead, so be careful.
How do I keep something alive in the face of an aggressive GC?
GC.KeepAlive() of course.

I am using Actions in C# and I was wondering if I need to set the instance of Action to null once I wish the GC to collect the objects properly?
Not necessarily. As long as no object is reachable which references the delegate, the delegate will be eligible for GC.
That being said, in your example, aClass and bClass are still valid variables, and refer to reachable objects. This means that aClass.a is still reachable, and not eligible for GC, so it will not be collected.
If you wanted these to be garbage collected, you would need to explicitly set the object reference (aClass) to null so that the A instance, and it's contained delegate, were no longer reachable objects, and then you'd have to explicitly call GC.Collect to trigger the GC, since nothing will cause the GC to trigger in your code.

Related

How to delete an instance of a class within that instance

class Foo
{
int Data;
public Foo(int data)
{
Data = data;
}
public void Diminish()
{
this.Data--;
}
}
Foo Foo1 = new Foo(5);
Foo1.Diminish();
Is there Any way delete Foo1 When Data is 0 within Foo1?
(Free up the memory used by that instance)
An object never decides when to "delete" or garbage collect itself. It can't know what else has a reference to it. When there are no references left to an object then it gets garbage collected.
In the scenario described in the question - suppose you had a List<Foo>() and then one instance of Foo spontaneously decided, based on its own internal logic, that it should no longer exist? I don't know what that would even look like - all references to it suddenly become null?
If that were possible, the defects it would cause would be beyond all mortal comprehension. We have enough trouble when two threads modify the same object or property because of improper/nonexistent locking, and so from the viewpoint of one thread something has changed when it shouldn't.
But this would create similar scenarios even in single-threaded applications.
var myFoo = new Foo(1);
myFoo.Diminish();
var x = myFoo.Data; // Null reference exception! WTH - it nullified itself!
A fundamental concept of OOP is that classes don't need to know too much about the internal state of other classes. But now you'd need to "know" that a class could suddenly choose to opt-out and jump off the cliff into the GC, and constantly null-check objects that already weren't null.
It's a very good thing that this is not possible.
As some comments and at least one answer explain, a normal reference in a .NET program is exactly what keeps that object alive. It is not possible to "delete" the object explicitly in the way you seem to mean.
An object will remain alive as long as it is "reachable" (i.e. it is possible to start with a "rooted" reference and find the reference to that object). Once it is no longer reachable (which could happen even when a reference to the object exists, as long as that reference can't be reached from a rooted reference…e.g. a circular reference between two objects does not prevent garbage collection of those two objects, as long as the only reference to each is from the other).
All of this is discussed in detail in documentation references provided elsewhere.
Now, all that said, depending on what you're actually trying to do, you might find that the WeakReference<T> class is what you need. This class provides a way to reference objects without preventing them from being garbage-collected. You can combine this with reference-counting and an object repository to maintain non-weak references to keep the object alive to produce the results you want.
For example:
class RefCounted
{
private static readonly HashSet<RefCounted> _alive = new HashSet<RefCounted>();
private int _referenceCount;
public RefCounted()
{
AddRef();
}
public AddRef()
{
if (_referenceCount == 0)
{
// the collection ensures a strong reference to the object, to prevent
// the object from being garbage-collected even if the only other
// references are weak references.
_alive.Add(this);
}
_referenceCount++;
}
public Release()
{
if (--_referenceCount)
{
// no longer need to force the object to stay alive; if the only remaining
// references to the object are weak references, then the garbage
// collector may collect the object.
_alive.Remove(this);
}
}
}
Then you can use it like this:
WeakReference<RefCounted> o = new WeakReference<RefCounted>(new RefCounted());
RefCounted r;
if (o.TryGetTarget(out r))
{
// do stuff with r
// done with the object? Then call:
r.Release();
}
It is important to note that this is not precisely what you're asking for. The above still does not give you deterministic control over the actual garbage collection of the object. Furthermore, once you retrieve the target of the weak reference into e.g. a local variable, you now hold a strong reference to the object for the lifetime of that local variable (i.e. up to the last point that variable is used) and the object cannot be collected during that lifetime, even if the reference count goes to 0 (though at that point the object will of course be collected once that variable no longer exists or is reachable).
But if you really have a scenario where you need to use some type of reference counting to track and in some way control the lifetime of an object, the above is how you'd do it. You would have to store the WeakReference<T> objects rather than direct references to T, retrieving the target reference at each place in the code where you need it.
Of course, there remains the question of why do you think you need reference counting. And even if you need reference counting, why should the reference count be directly related to the lifetime of the object? Your question does not explain these aspects, and so I can't really address them directly. I will say that reference counting is not needed at all for effective management of managed objects in .NET programs. As long as you don't actively interfere with the maintenance and use of references to managed objects, the .NET garbage collection system will do a far better job of dealing with the lifetimes of objects than you or I could.
An object instance will remain "alive" as long as any piece of code references it. So, it's something you cannot really control from within a class.
From MSDN:
The .NET Framework's garbage collector manages the allocation and release of memory for your application. Each time you create a new object, the common language runtime allocates memory for the object from the managed heap. As long as address space is available in the managed heap, the runtime continues to allocate space for new objects. However, memory is not infinite. Eventually the garbage collector must perform a collection in order to free some memory. The garbage collector's optimizing engine determines the best time to perform a collection, based upon the allocations being made. When the garbage collector performs a collection, it checks for objects in the managed heap that are no longer being used by the application and performs the necessary operations to reclaim their memory.
So the garbage collector will take care of releasing the object when it goes out of scope unless you need to release some expensive (memory consumption) resource. You can sets the reference of the instance to null, this will terminate the object, unless there is some other reference to the object:
Foo Foo1 = new Foo(5);
Foo1.Diminish();
if(Foo1.Data == 0)
{
Foo1 = null;
}
C# is a managed language, you can't "delete" anything, once something can no longer be referenced the value clean up will be handled by the Garbage Collector.
There are edge cases where you need to handle clean up, or be more aware of how you are handling creation:
Instances of classes that implement IDisposable interface should be disposed of by calling the Dispose() method or wrapped in a using statement
Unmanaged resource wrappers, such as IntPtr may need to be cleaned up using Marshal.FreeHGlobal(IntPtr) or Marshal.FreeCoTaskMem(IntPtr) if you are required to manage them and you're making a call to unmanaged code
Static (and possibly long living) values will never be un-referenceable unless you actively set variables to null or remove items from collections

In what situation(s) would a reference point to an object that was queued for garbage collection?

I'm reading through a C# topic on Dispose() and ~finalize and when to use which. The author argues that you should not use references within your ~finalize because it's possible the object you're referencing may already be collected. The example specifically stated is, ".. you have two objects that have references to each other. If object #1 is collected first, then object #2's reference to it is pointing to an object that's no longer there."
In what scenarios would an instance of an object be in a state where it has a reference in memory to an object that is being GC'd? My guess is there are at least two different scenarios, one where the object reference is pointing at an object and one where the object reference is pointing at another object reference (eg. when it was passed by ref in a method).
You can have objects that reference each other, and the entire set can be eligible for GC.
Here is a simple code sample:
class Test {
public Test Other { get; set;}
static void Main()
{
Test one = new Test();
Test two = new Test { Other = one; }
one.Other = two;
one = null;
two = null
// Both one and two still reference each other, but are now eligible for GC
}
}
Normally the GC will only reclaim memory for objects that don't have any reference pointing to them. However, objects with finalizers are treated differently.
Here's what MSDN says about it:
Reclaiming the memory used by objects with Finalize methods requires
at least two garbage collections. When the garbage collector performs
a collection, it reclaims the memory for inaccessible objects without
finalizers. At this time, it cannot collect the inaccessible objects
that do have finalizers. Instead, it removes the entries for these
objects from the finalization queue and places them in a list of
objects marked as ready for finalization. [...]
The garbage collector calls the Finalize methods for the objects in this list and then removes the entries from the list. A future garbage collection will determine that the finalized objects are truly garbage because they are no longer pointed to by entries in the list of objects marked as ready for finalization.
So there is no guarantee that other objects referenced in a finalizer will still be usable when the Finalize method gets executed by the GC, since they may already have been finalized during an earlier garbage collection while the object itself was waiting to be finalized.
In short, objects that are not reachable from a GC root (static field, method parameter, local variable, enregistered variable) by following the chain of references are eligible to garbage collection. So it is fully possible that, say, object A refers to B that refers to C that refers to D, but suddenly A nulls out its reference to B, in which case B, C and D all can be collected.
Unfortunately, there is a lot of sloppy use of terminology surrounding garbage collection, which causes much confusion. A "disposer" or "finalizer" does not actually destroy an object, but rather serves to delay the destruction of an object which would otherwise be eligible for destruction, until after it has had a chance to put its affairs in order (i.e. generally by letting other things know that their services are no longer required).
It's simplest to think of the "stop the world" garbage collector as performing the following steps, in order:
Untag all items which are new enough that they might be considered "garbage".
Visit every garbage-collection root (i.e. thing which is inherently "live"), and if it hasn't been copied yet, copy it to a new heap, update the reference to point to the new object, and and visit all items to which it holds references (which will copy them if they haven't been copied). If one visits an item in the old heap that had been copied, just update the reference one used to visit it.
Examine every item that has registered for finalization. If it hasn't yet been copied, unregister it for finalization, but append a reference to it on a list of objects which need to be finalized as quickly as possible.
Items on the immediate-finalization list are considered "live", but since they haven't yet been copied yet, visit every item on that list and, if not yet copied, copy it to the new heap and visit all items to which it holds references.
Abandon the old heap, since nobody will hold references to anything on it anymore.
It's interesting to note that while some other garbage-collection systems work by using double-indirected pointers for references, the .net garbage collector (at least the normal "stop the world" one) uses direct pointers. This increases somewhat the amount of work the collector has to do, but it improves the efficiency of code that manipulates objects. Since most programs spend more of their time manipulating objects than they spend collecting garbage, this is a net win.
" ... then object #2's reference to it is pointing to an object that's no longer there."
That will never happen. Whenever your code has access to a reference then the object being referred to still exists. This is called memory safety and this still holds when an object is being finalized in the background. A reference never points to a collected instance.
But an existing, non collected object could already have been Finalized (Disposed). That is probably what your warning refers to.
class Foo
{
private StreamWriter logFile = ...
private StringBuilder sb = new StringBuilder("xx");
~Foo()
{
if (sb.ToString() == "xx") // this will always be safe and just work
{
// the next line might work or
// it might fail with "logFile already Disposed"
logFile.Writeline("Goodbye");
}
}
}
There is a term called resurrection in .NET.
In short, resurrection may happen when your object is in finalization queue but when the finalizer (~ClassName() method) is called, it moves the object back to the game. For example:
public class SomeClass
{
public static SomeClass m_instance;
...
~SomeClass()
{
m_instance = this;
}
}
You can read more on this here: Object Resurrection using GC.ReRegisterForFinalize. But I would really recommend the book CLR via C# by Jeffrey Richter as it explained this subject in depth in one of the chapters.

Nesting reference type object in inner scope has no effect on garbage collection: True or False?

I have been reading the Garbage Collection chapter from Jeffrey Richter's fine book, "CLR via C#". There, he illustrated an example of how the GC conceptually works (how roots are marked) by referring to a disassembly listing of native code emitted from the JIT compiler. From that example, it occurred to me that nesting reference types in scope seems to have ZERO effect on expediting the garbage collection of the nested variable. I wonder if I am understanding this correctly. In any case, consider these 2 versions of code:
A) Nesting a reference type variable (y) in an inner scope:
namespace scope
{
class A { public void foo() { } }
class Program
{
static void Main(string[] args)
{
A x = new A();
x.foo();
{
A y = new A();
y.foo();
}
}
}
}
B) Same as above, except that x and y are in the same scope.
namespace scope
{
class A { public void foo() { } }
class Program
{
static void Main(string[] args)
{
A x = new A();
x.foo();
A y = new A();
y.foo();
}
}
}
Out of curiosity, I checked the generated IL code for both versions, and they are the SAME!
Q1: So, this seems to imply that indeed, scoping doesn't expedite garbage collection in any way. Is this correct? (Note: I know about the "using" statement - but I'm only curious about the behaviour on the GC of "plain old scoping" as illustrated in the 2 examples above.)
Q2: If the answer to Q1 is "True", then I am utterly perplexed by how a "Object Lifetime is Not Determined by Scope" situation can possibly happen, as described here:
http://www.curly-brace.com/favorite.html
Garbage collection partly depends on whether you're running in the debugger or not. I'll assume we're not.
It can actually be much more aggressive than you think. For example:
object o = new object();
Console.WriteLine(o);
Console.WriteLine("hi");
o = new object();
The object can be garbage collected immediately after the second line - i.e. long before the variable o exits scope. This would also be true without the final line.
In other words, scoping doesn't expedite garbage collection - because the GC is already smarter than you might think.
In fact, the garbage collector can be even more aggressive. Consider this code:
Foo f = new Foo();
f.SomeMethod();
Console.WriteLine("Hello");
where Foo looks like this:
public class Foo
{
int x = 10;
public void SomeMethod()
{
Console.WriteLine(x);
for (int i = 0; i < 100; i++)
{
Console.WriteLine("Hello");
Thread.Sleep(100);
}
}
}
In theory, the garbage collector can collect the Foo object while SomeMethod is running so long as it's gone past the first line, where it actually reads from x. If Foo had a finalizer, you could have the finalizer running in one thread while SomeMethod was running in another. Scary stuff.
Jon is, of course correct. For some additional background I refer you to the C# specification, which states:
The actual lifetime of a local variable is implementation-dependent. For example, a compiler might statically determine that a local variable in a block is only used for a small portion of that block. Using this analysis, the compiler could generate code that results in the variable’s storage having a shorter lifetime than its containing block.
and, also important:
The storage referred to by a local reference variable is reclaimed independently of the lifetime of that local reference variable.
Notice here that we are drawing a distinction between the lifetime of the object being referenced -- that is, when the garbage collector is allowed to step in -- and the lifetime of the variable holding the reference. As Jon points out, the lifetime of both the variable and the object it refers to can be aggressively shortened by the compiler and the garbage collector both if we can prove that doing so does not release a reference that someone could still be holding on to.
The author of the linked page appears to think that C# is (or should be) in some way related to C++. From such a position it is not surprising that 'astonishing' (in the Principle of Least Astonishment sense) things follow. In particular,
A ReadAccessor is an object which provides access to the pixel data in an image, which is stored in a memory mapped file for performance reasons.
should suggest immediately (at a really quite basic level of knowledge of .NET) that ReadAccessor implement IDisposable.
Object life-time doesn't need scope, it is based on references to that object and it's current generation count in the GC - once it is completely dereferenced, it's time-to-GC just depends on its generation.
Method scoped references will simply be completely dereferenced by the end of the method (in your example).
Update: or as Jon points out, if it's completely derefenced before then, it is elligible for GC.

Can a conforming C# compiler optimize away a local (but unused) variable if it is the only strong reference to an object?

See also these related resources:
Does the .NET garbage collector perform predictive analysis of code? (on Stack Overflow)
WP7: When does GC Consider a Local Variable as Garbage (blog article on MSDN)
In other words:
Can an object referenced by a local
variable be reclaimed before the
variable goes out of scope (eg.
because the variable is assigned, but
then not used again), or is that
object guaranteed to be ineligible for
garbage collection until the variable
goes out of scope?
Let me explain:
void Case_1()
{
var weakRef = new WeakReference(new object());
GC.Collect(); // <-- doesn't have to be an explicit call; just assume that
// garbage collection would occur at this point.
if (weakRef.IsAlive) ...
}
In this code example, I obviously have to plan for the possibility that the new'ed object is reclaimed by the garbage collector; therefore the if statement.
(Note that I'm using weakRef for the sole purpose of checking if the new'ed object is still around.)
void Case_2()
{
var unusedLocalVar = new object();
var weakRef = new WeakReference(unusedLocalVar);
GC.Collect(); // <-- doesn't have to be an explicit call; just assume that
// garbage collection would occur at this point.
Debug.Assert(weakRef.IsAlive);
}
The main change in this code example from the previous one is that the new'ed object is strongly referenced by a local variable (unusedLocalVar). However, this variable is never used again after the weak reference (weakRef) has been created.
Question: Is a conforming C# compiler allowed to optimize the first two lines of Case_2 into those of Case_1 if it sees that unusedLocalVar is only used in one place, namely as an argument to the WeakReference constructor? i.e. is there any possibility that the assertion in Case_2 could ever fail?
It doesn't matter what the C# compiler does - the JITter/GC are allowed to clean up local references once they're no longer alive in a method body. Look at the docs for GC.KeepAlive
Also, this powerpoint presentation, especially from slide 30 onwards, helps to explain what the JIT/GC can get up to.
While my question has been answered, I thought I'd post this relevant piece of information I just found on the MSDN blog article "WP7: When does GC Consider a Local Variable as Garbage" by abhinaba:
[T]he ECMA specification (ECMA 334 Section 10.9) […] states
“For instance, if a local variable that is in scope is the only existing reference to an object, but that local variable is never referred to in any possible continuation of execution from the current execution point in the procedure, an implementation might (but is not required to) treat the object as no longer in use.”
This says it all. The mentioned article also says that the .NET framework (at least in Release mode) will perform predictive analysis and free such objects, while the .NET Compact Framework won't (for performance reasons).
Is a conforming C# compiler allowed to optimize the first two lines of Case_2 into those of Case_1 if it sees that unusedLocalVar is only used in one place, namely as an argument to the WeakReference constructor?
The two definitions are equivalent so transforming from one to the other is not an "optimization" because neither is more efficient.
i.e. is there any possibility that the assertion in Case_2 could ever fail?
Yes. A production compiler is not likely to retain a reference unnecessarily so it will be removed, the GC will not see it as a global root and will collect that object.
Note that garbage collectors do not see your program in terms of variables and scope. Those high-level concepts have long since been compiled away by the time your code gets to the garbage collector. The GC sees only registers, thread stacks and global variables.

Setting an object to null vs Dispose()

I am fascinated by the way the CLR and GC works (I'm working on expanding my knowledge on this by reading CLR via C#, Jon Skeet's books/posts, and more).
Anyway, what is the difference between saying:
MyClass myclass = new MyClass();
myclass = null;
Or, by making MyClass implement IDisposable and a destructor and calling Dispose()?
Also, if I have a code block with a using statement (eg below), if I step through the code and exit the using block, is the object disposed of then or when a garbage collection occurs? What would happen if I call Dispose() in the using block anyay?
using (MyDisposableObj mydispobj = new MyDisposableObj())
{
}
Stream classes (eg BinaryWriter) have a Finalize method? Why would I want to use that?
It's important to separate disposal from garbage collection. They are completely separate things, with one point in common which I'll come to in a minute.
Dispose, garbage collection and finalization
When you write a using statement, it's simply syntactic sugar for a try/finally block so that Dispose is called even if the code in the body of the using statement throws an exception. It doesn't mean that the object is garbage collected at the end of the block.
Disposal is about unmanaged resources (non-memory resources). These could be UI handles, network connections, file handles etc. These are limited resources, so you generally want to release them as soon as you can. You should implement IDisposable whenever your type "owns" an unmanaged resource, either directly (usually via an IntPtr) or indirectly (e.g. via a Stream, a SqlConnection etc).
Garbage collection itself is only about memory - with one little twist. The garbage collector is able to find objects which can no longer be referenced, and free them. It doesn't look for garbage all the time though - only when it detects that it needs to (e.g. if one "generation" of the heap runs out of memory).
The twist is finalization. The garbage collector keeps a list of objects which are no longer reachable, but which have a finalizer (written as ~Foo() in C#, somewhat confusingly - they're nothing like C++ destructors). It runs the finalizers on these objects, just in case they need to do extra cleanup before their memory is freed.
Finalizers are almost always used to clean up resources in the case where the user of the type has forgotten to dispose of it in an orderly manner. So if you open a FileStream but forget to call Dispose or Close, the finalizer will eventually release the underlying file handle for you. In a well-written program, finalizers should almost never fire in my opinion.
Setting a variable to null
One small point on setting a variable to null - this is almost never required for the sake of garbage collection. You might sometimes want to do it if it's a member variable, although in my experience it's rare for "part" of an object to no longer be needed. When it's a local variable, the JIT is usually smart enough (in release mode) to know when you're not going to use a reference again. For example:
StringBuilder sb = new StringBuilder();
sb.Append("Foo");
string x = sb.ToString();
// The string and StringBuilder are already eligible
// for garbage collection here!
int y = 10;
DoSomething(y);
// These aren't helping at all!
x = null;
sb = null;
// Assume that x and sb aren't used here
The one time where it may be worth setting a local variable to null is when you're in a loop, and some branches of the loop need to use the variable but you know you've reached a point at which you don't. For example:
SomeObject foo = new SomeObject();
for (int i=0; i < 100000; i++)
{
if (i == 5)
{
foo.DoSomething();
// We're not going to need it again, but the JIT
// wouldn't spot that
foo = null;
}
else
{
// Some other code
}
}
Implementing IDisposable/finalizers
So, should your own types implement finalizers? Almost certainly not. If you only indirectly hold unmanaged resources (e.g. you've got a FileStream as a member variable) then adding your own finalizer won't help: the stream will almost certainly be eligible for garbage collection when your object is, so you can just rely on FileStream having a finalizer (if necessary - it may refer to something else, etc). If you want to hold an unmanaged resource "nearly" directly, SafeHandle is your friend - it takes a bit of time to get going with, but it means you'll almost never need to write a finalizer again. You should usually only need a finalizer if you have a really direct handle on a resource (an IntPtr) and you should look to move to SafeHandle as soon as you can. (There are two links there - read both, ideally.)
Joe Duffy has a very long set of guidelines around finalizers and IDisposable (co-written with lots of smart folk) which are worth reading. It's worth being aware that if you seal your classes, it makes life a lot easier: the pattern of overriding Dispose to call a new virtual Dispose(bool) method etc is only relevant when your class is designed for inheritance.
This has been a bit of a ramble, but please ask for clarification where you'd like some :)
When you dispose an object, the resources are freed. When you assign null to a variable, you're just changing a reference.
myclass = null;
After you execute this, the object myclass was referring to still exists, and will continue to until the GC gets around to cleaning it up. If Dispose is explicitly called, or it's in a using block, any resources will be freed as soon as possible.
The two operations don't have much to do with each others.
When you set a reference to null, it simply does that. It doesn't in itself affect the class that was referenced at all.
Your variable simply no longer points to the object it used to, but the object itself is unchanged.
When you call Dispose(), it's a method call on the object itself. Whatever the Dispose method does, is now done on the object. But this doesn't affect your reference to the object.
The only area of overlap is that when there are no more references to an object, it will eventually get garbage collected. And if the class implements the IDisposable interface, then Dispose() will be called on the object before it gets garbage collected.
But that won't happen immediately after you set your reference to null, for two reasons.
First, other references may exist, so it won't get garbage collected at all yet, and second, even if that was the last reference, so it is now ready to be garbage collected, nothing will happen until the garbage collector decides to delete the object.
Calling Dispose() on an object doesn't "kill" the object in any way. It is commonly used to clean up so that the object can be safely deleted afterwards, but ultimately, there is nothing magical about Dispose, it's just a class method.

Categories

Resources