0

I am trying to figure out how to control when my custom objects are collected by the Garbage Collector - I have found a lot of references to using IDisposable/Destructors to do this but every example has something like the following:

class Car
{
    ~Car()  // destructor
    {
        // cleanup statements...
    }
}

(http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx)

What actually goes in the "Cleanup Statements"?

I want to be able to call CarInstance.Dispose() when my program is down with an instance of the object and have the GC clean up that specific instance - that way I won't have spikes in performance issues when the GC runs automatically and cleans up a bunch -

Let me know! William

William
  • 3,335
  • 9
  • 42
  • 74
  • I like this SO link - it has some great links about the whole subject and touches upon both destructors and IDisposable: http://stackoverflow.com/questions/3649066/use-of-destructor-in-c – dash Dec 01 '11 at 17:12

6 Answers6

2

You should only create a finalizer if you need to clean up unmanaged resources that .Net cannot clean automatically.

You should implement IDisposable if you need to clean up expensive managed or unmanaged resources. Expensive doesn't mean memory; an "expensive" managed resource means a wrapper around something unmanaged (eg, files, streams, GDI+, etc)

It is not possible to force the GC to collect a specific object.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
1

It depends on what you mean by "cleanup" -

If you're just referring to releasing managed memory, then there is no direct way to cleanup the memory for your objects, and just your objects. In fact, you wouldn't want to do this - the GC is highly efficient, and trying to "control" it tends to mess up its heuristics. This is part of why calling GC.Collect directly is a bad idea.

If you're managing resources, such as native resources, or other similar concepts that must be released, then IDisposable is the way to go. You should implement it properly, though, which is very different than your sample code. For details on proper implementation, you can see my series on IDisposable.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
1

You should not try to 'control the GC', except in extremely rare cases. Almost guaranteed you won't run into such a case. Ever.

IDisposable.Dispose() is not really (directly) related to the GC or a destructor.

  • Dispose() is for cleaning up managed resources, other than memory. It needs to be explicitly called by your code. As a backup, some classes put calls to it in a destructor. But it should be called before that.
  • A destructor is for cleaning up unmanaged resources, usually also other than memory. But you should probably do this in Dispose(), too.
  • The GC runs automatically and does its job well - it cleans up the memory that your objects use. You should generally not try to fuss with it.
Andrew Barber
  • 39,603
  • 20
  • 94
  • 123
0

If you want to control when a specific object is disposed, implement IDisposable for it and call obj.Dispose() explicitly.

Daniel Peñalba
  • 30,507
  • 32
  • 137
  • 219
0

To call CarInstance.Dispose(), it must implement IDisposable The finalizer should then call Dispose(false), so that all of the cleanup code is in one location.

Follow the Finalize / Dispose pattern referenced in this question.

As for what should be in the "Cleanup Statements," this should contain code that cleans up any unmanaged resources. Things that .NET can't clean up itself.

Community
  • 1
  • 1
davisoa
  • 5,407
  • 1
  • 28
  • 34
0

Here is a snippet of code for a base class that I use for my Repository. It makes use of the IDisposable interface to clean up the Linq-to-SQL context.

    /// <summary>
/// Base class for common functionality shared across Sql repositories.
/// </summary>
internal abstract class BaseSqlRepository : IDisposable
{
    #region Members
    /// <summary>
    /// Linq to Sql data context
    /// </summary>
    private SqlRepositoryDataContext context;

    /// <summary>
    /// Determines whether the class has invoked the dispose/finalize functionality.
    /// </summary>
    private bool isDisposed;
    #endregion

    #region Constructors
    /// <summary>
    /// Initializes a new instance of the <see cref="BaseSqlRepository"/> class.
    /// </summary>
    protected BaseSqlRepository()
    {
        this.context = new SqlRepositoryDataContext(InitializeConnectionString());
        this.isDisposed = false;
    }

    protected BaseSqlRepository(SqlRepositoryDataContext Context)
    {
        this.context = Context;
        this.isDisposed = false;
    }

    /// <summary>
    /// Finalizes an instance of the BaseSqlRepository class.
    /// Releases unmanaged resources and performs other cleanup operations before the
    /// <see cref="BaseSqlRepository"/> is reclaimed by garbage collection.
    /// </summary>
    ~BaseSqlRepository()
    {
        this.Dispose(false);
    }

    #endregion

    #region Properties
    /// <summary>
    /// Gets or sets the context.
    /// </summary>
    /// <value>The context.</value>
    protected SqlRepositoryDataContext Context
    {
        get { return this.context; }
        set { this.context = value; }
    }
    #endregion

    #region Methods
    /// <summary>
    /// Initializes the connection string.
    /// </summary>
    /// <returns>Connection string.</returns>
    protected static string InitializeConnectionString()
    {
        string connectionName = ConfigurationManager.AppSettings["AppConnection"];
        string connection = string.Empty;

        if (!string.IsNullOrWhiteSpace(connectionName))
        {
            connection = ConfigurationManager.ConnectionStrings[connectionName].ConnectionString;
            if (string.IsNullOrWhiteSpace(connection))
            {
                throw new ArgumentException("Unable to initialize a connection to the database.");
            }
        }
        else
        {
            throw new ArgumentException("Unable to initialize a connection to the database.");
        }

        return connection;
    }

    /// <summary>
    /// Releases unmanaged and - optionally - managed resources
    /// </summary>
    /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
    protected void Dispose(bool disposing)
    {
        if (!this.isDisposed && disposing)
        {
            // Dispose the managed resources of the class
            this.context.Dispose();
        }

        // Dipose the un-managed resources of the class
        this.isDisposed = true;
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }
    #endregion
}
Novus
  • 197
  • 2
  • 15