title: Is GC.SuppressFinalize guaranteed?
body: <p>My observation in practice has been that <a href="http://msdn.microsoft.com/en-us/library/system.gc.suppressfinalize.aspx" rel="nofollow noreferrer"><code>GC.SuppressFinalize</code></a> does not always suppress the call to the finalizer. It could be that the finalizer gets called nontheless. I wonder therefore whether <code>GC.SuppressFinalize</code> has the nature of a <em>request</em> rather than a <em>guarantee</em> by the system?</p>
  
  <hr>
  
  <p><strong>More Information</strong></p>
  
  <p>Following information may help provide more context for the quesiton if needed.</p>
  
  <p>The <a href="http://msdn.microsoft.com/en-us/library/system.gc.suppressfinalize.aspx" rel="nofollow noreferrer"><code>GC.SuppressFinalize</code></a> document summary does state that is a request:</p>
  
  <blockquote>
    <p>Requests that the system not call the
    finalizer for the specified object.</p>
  </blockquote>
  
  <p>I wonder if this was a casual use of the word or truly intended to describe the run-time behavior.</p>
  
  <p>I have observed this with the following <code>SingletonScope</code> class taken from the <a href="http://code.google.com/p/schnell/" rel="nofollow noreferrer">Schnell</a> project, which was based on an <a href="http://www.interact-sw.co.uk/iangblog/2004/04/26/yetmoretimedlocking" rel="nofollow noreferrer">original idea by Ian Griffiths</a> except it is more generalized. The idea is to detect, in debug builds, if the <code>Dispose</code> method did get called or not. If not, the finalizer would kick in eventually and one can put up a warning. If <code>Dispose</code> is called then <code>GC.SuppressFinalize</code> <em>should</em> prevent the finalizer from firing. Unfortunately, the warnings seem to fire anyhow, but not in a deterministic fashion. That is, they don't fire on each and every run.</p>
  
  <pre><code>#region License, Terms and Author(s)
  //
  // Schnell - Wiki widgets
  // Copyright (c) 2007 Atif Aziz. All rights reserved.
  //
  //  Author(s):
  //      Atif Aziz, http://www.raboof.com
  //
  // This library is free software; you can redistribute it and/or modify it 
  // under the terms of the GNU Lesser General Public License as published by 
  // the Free Software Foundation; either version 2.1 of the License, or (at 
  // your option) any later version.
  //
  // This library is distributed in the hope that it will be useful, but WITHOUT
  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
  // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  // License for more details.
  //
  // You should have received a copy of the GNU Lesser General Public License
  // along with this library; if not, write to the Free Software Foundation, 
  // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
  //
  #endregion
  
  namespace WikiPad
  {
      #region Imports
  
      using System;
      using System.Diagnostics;
  
      #endregion
  
      //
      // NOTE: To use SingletonScope and ISingletonScopeHelper with value 
      // types, use Nullable&lt;T&gt;. For example, if the type of value to scope
      // is ThreadPriority then use ISingletonScopeHelper&lt;ThreadPriority?&gt;
      // and SingletonScope&lt;ThreadPriority?&gt;.
      //
  
      //
      // In debug builds, this type is defined as a class so a finalizer
      // can be used to detect an undisposed scope.
      //
  
      /// &lt;summary&gt;
      /// Designed to change a singleton and scope that change. After exiting
      /// the scope, the singleton is restored to its value prior to entering
      /// the scope.
      /// &lt;/summary&gt;
  
      #if !DEBUG
      internal struct SingletonScope&lt;T, H&gt; 
      #else
      internal sealed class SingletonScope&lt;T, H&gt; 
      #endif
          : IDisposable 
          where H : ISingletonScopeHelper&lt;T&gt;, new()
      {
          private T _old;
  
          public SingletonScope(T temp)
          {
              _old = Helper.Install(temp);
          }
  
          private static H Helper
          {
              get { return new H(); }
          }
  
          public void Dispose()
          {
              //
              // First, transfer fields to stack then nuke the fields.
              //
  
              var old = _old;
              _old = default(T);
  
              //
              // Shazam! Restore the old value.
              //
  
              Helper.Restore(old);
  
              #if DEBUG
              GC.SuppressFinalize(this); // Only when defined as a class!
              #endif
          }
  
          #if DEBUG
  
          //
          // This finalizer is used to detect an undisposed scope. This will
          // only indicate that the scope was not disposed but (unfortunately)
          // not which one and where since GC will probably collect much later
          // than it should have been disposed.
          //
  
          ~SingletonScope()
          {
              Debug.Fail("Scope for " + typeof(T).FullName + " not disposed!");
          }
  
          #endif
      }
  }
  </code></pre>
  
  <hr>
  
  <p>A full working example is available at <a href="http://gist.github.com/102424" rel="nofollow noreferrer">http://gist.github.com/102424</a> with compilation instructions, but do note that the problem cannot be reproduced deterministically so far.</p>