Q:

c# destructors (or finalisers?)  called automatically at the end of my program - should I do somthing about that? 

So I have been reading loads of posts regarding how to deal with memory allocation and finalisers etc... I think I am clear on it, until I saw the debug that I get from running my program.  The program itself is on a secure network and my "internet access" PC does not have C# so the un-tested example below may not give exactly the same behaviour, but hopefully serves to show what I am seeing:
I have two files:
cli.cs
for (int i = 1; i <= 3; i++)
{
    Console. WriteLine("------ ITERATION " + i + "------");
    Regression. Tests. FileOperations.runTest();
}
Console. WriteLine("Test Finished");

FileOperations.cs
class FileOperations
{
    Obj1 obj1 = null;
    Obj2 obj2 = null;

    public void doStuff()
    {
        // Clear the objects
        obj1 = null;
        obj2 = null;

        // Create the objects
        obj1 = new Obj1();
        obj2 = new Obj2();

        // do stuff (in this case just sleep for 5 seconds)
        Thread. Sleep(5000);
    }

    // Static function so its can be called without instance
    static public void runTest()
    {
        FileOperations fileOps = new FileOperations();
        fileOps.doStuff();
    }
}

So in cli.cs we call the static function runTest() in a loop which
iterates 3 times. 
runTest() instantiates a FileOperations object and
then calls its doStuff() function. 
doStuff() Clears any previous objects by setting them to null (I read this is the way to do that).  Then it creates new instances of the objects for use. 

So I tested this and it all seems to work ok.  However the output to my program gives me this debug:
------ ITERATION 1 ------
------ ITERATION 2 ------
------ ITERATION 3 ------
Test Finished
~Obj1()
~Obj2()
~Obj1()
~Obj2()
~Obj1()
~Obj2()

So, if I am clearing the objects correctly (which I may not be?)  why are all my Obj1 and Obj2's only getting deleted at the end of the programs? -  maybe this is correct? -  I have not managed to find any examples of people with the same output...

A:

You have only three iterations, so there aren´t many point in time in your program when Garabage-Collection may take place.  So even if GC would kick in three times (which I doubt in this little scenario) you could not determine if that happens before the next iteration or just after all of them.  The process of GarbageCollection is quite complex and in particual un-deterministic, so you can´t determine when exactly an instance is freed from memory.  
Although you have three different instances of FileOperations which hold their own instances of Obj1 and Obj2 those instances may or may not remain on the heap, until GC finally decides to kick in.  This happens at least when your app terminates, but usually far earlier.  The only thing you can expect from GC is that it will run.  The eraliest point for the GC to work on the other side is when an instance gets out of scope and no more references to that instance exist.  In your case this is when an instance of FileOperations is not used any more in your code.  
So you have more or less two options in your scenario:

GC working on every iteration or at least some of them, at least far earlier then your app terminates
when your app terminates

As your program is so little chances are very high that the second option happens.  As an aside this improves overall performance, as GC does not need to work - or in particular free any instances - all the time, just a few (or in your case probably one) time(s).