Decompiliamo con Reflector e 
vediamo come apparirebbe, nei diversi casi, il metodo 
ReadMessage se fosse scritto in C#.
Nel primo caso reader è stato dichiarato come un handle, al 
quale abbiamo è stato assegnato un oggetto allocato sul managed 
heap tramite l'operatore gcnew       
       
 . Dopo essere stato utilizzato, l'oggetto viene "distrutto" con l'operatore 
delete. Tale 
operatore viene sostanzialmente           
  tradotto in un'eventuale chiamata al metodo 
Dispose (per gli oggetti il cui tipo implementa IDisposable*). 
Come noto, tale metodo nasce con lo scopo fondamentale di rilascare le 
risorse utilizzate dal nostro oggetto, non la memoria. 
La deallocazione sarà gestita dal garbage collector, che in 
questo caso, vedendo che è già stato eseguito il cleanup, non chiamerà  
il distruttore o l'eventuale finalizzatore **. Codice C#:
public void ReadMessage(string path)
{
      int num1;
      Stream stream1 = null;
      StreamReader reader1 = null;
      stream1 = typeof(Person).Assembly.GetManifestResourceStream(path);
      reader1 = new StreamReader(stream1);
      Console.WriteLine(reader1.ReadLine());
      IDisposable disposable1 = reader1;
      if (disposable1 != null)
      {
            disposable1.Dispose();
            num1 = 0;
      }
      else
      {
            num1 = 0;
      }
}
Nel secondo caso, invece, non distruggendo l'oggetto, lasciamo che 
sia il garbage collector a scaricare le risorse invocando il distruttore prima della finalizzazione. Ciò 
signfica che fino a un tempo imprecisato le nostre risorse 
rimarrano utilizzate   
           
     . Codice C#:
public void ReadMessage(string path)
{
      Stream stream1 = null;
      StreamReader reader1 = null;
      stream1 = typeof(Person).Assembly.GetManifestResourceStream(path);
      reader1 = new StreamReader(stream1);
      Console.WriteLine(reader1.ReadLine());
}
Nel terzo caso invece vediamo un reference type object 
dichiarato come se fosse di un tipo valore. In 
realtà, questa è una semplice "illusione" propinataci dal compilatore. Il nostro 
oggetto è allocato sul managed heap, ma la semantica che esso 
assume, dal nostro punto di vista, è quella di un oggetto sullo stack. Questo ha 
un'importante implicazione: il compilatore genera automaticamente il 
codice per rilasciare le risorse usate dal nostro oggetto (ovviamente 
solo se necessario, ovvero se implementa IDisposable ), 
esattamente come il C++ nativo inserisce una chiamata ai distruttori delle 
variabili automatiche alla fine del metodo. Questo idioma, noto come 
deterministic finalization, è diventato in 
C++/CLI un nuovo idioma, analogo ma differente (a causa 
della diversa implementazione dell'operatore delete), 
detto deterministic cleanup           
             
      . Codice C#: 
public void ReadMessage(string path)
{
      Stream stream1 = null;
      StreamReader reader1 = null;
      stream1 = typeof(Person).Assembly.GetManifestResourceStream(path);
      StreamReader modopt(IsConst) local1 = (StreamReader modopt(IsConst)) new StreamReader(stream1);
      try
      {
            reader1 = local1;
            Console.WriteLine(reader1.ReadLine());
      }
      fault
      {
            reader1.Dispose();
      }
      reader1.Dispose();
}
* In C++/CLI una classe dichiarata con un distruttore, è mappata 
automaticamente in una classe che implementa IDisposable e il suo distruttore è    
   mappato nel metodo Dispose.
** In C++/CLI è possibile dichiarare un 
finalizzatore         
            
. In questo modo, quando l'oggetto sta per essere distrutto non in maniera 
deterministica, bensì appena prima di una garbage collection è possibile svolgere un po' di custom behavior 
(per esempio tracciarlo)... oltre a invocare, com'è naturale, il distruttore affinchè vengano rilasciate le risorse.
Questo è possibile perchè il compilatore genera in questo modo il 
metodo Dispose:
protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool flag1)
{
      if (flag1)
      {
            this.~Person();
      }
      else
      {
            try
            {
                  this.!Person();
            }
            finally
            {
                  base.Finalize();
            }
      }
}
Anzichè quello che avremmo in assenza di un 
finalizzatore      :
protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool flag1)
{
      if (flag1)
      {
            this.~Person();
      }
      else
      {
            base.Finalize();
      }
}
Vediamo quindi il seguente esempio:
using namespace System;
public ref class Person
{
private:
    String^ name_;
public:
    Person (String^ name): name_ (name)
    {
    }
    ~Person ()
    {
        Console::WriteLine (name_ + ": I'm releasing resources...!");
    }
    !Person ()
    {
        Console::WriteLine (name_ + ": I'm going to be finalized...!");
        Console::WriteLine (name_ + ": I will call the destructor...!");
        delete this;
    }
};
void DeterministicCleanupBehavior ()
{
    Person person ("Guy on the heap with stack semantic");
}
void NonDeterministicCleanupBehavior ()
{
    Person^ person = gcnew Person ("Guy on the heap");
}
int main(array<System::String ^> ^args)
{
    Console::WriteLine ("Began!");
    DeterministicCleanupBehavior ();
    NonDeterministicCleanupBehavior ();
    Console::WriteLine ("Ended!");
    return 0;
}
Produrrà il seguente output:
  Began!
Guy on the heap with stack semantic: I'm releasing 
  resources...!
Ended!
Guy on the heap: I'm going to be 
  finalized...!
Guy on the heap: I will call the destructor...!
Guy on the 
  heap: I'm releasing resources...!
In realtà non potremmo prevedere quando viene 
finalizzato dal garbage collector il nostro "guy on the heap"... ma essendo il 
programma molto breve è naturale che venga scaricato soltanto alla fine 
dell'esecuzione (è ovvio che se dopo la chiamata a 
NonDeterministicCleanupBehavior il nostro programma eseguisse una 
lunga serie di istruzioni sarebbe probabile che il "guy on the heap" venga 
finalizzato in un qualche istante prima della terminazione del 
processo).