【问题标题】:Cleaning up Resources (Garbage Collection, Using, IDisposable, etc)清理资源(垃圾收集、使用、IDisposable 等)
【发布时间】:2011-12-02 01:19:49
【问题描述】:
我正在尝试弄清楚如何控制垃圾收集器何时收集我的自定义对象 - 我发现了很多使用 IDisposable/Destructors 来执行此操作的参考,但每个示例都有以下内容:
class Car
{
~Car() // destructor
{
// cleanup statements...
}
}
(http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx)
“清理声明”中的实际内容是什么?
我希望能够在我的程序因对象实例而关闭时调用 CarInstance.Dispose() 并让 GC 清理该特定实例 - 这样我就不会在 GC 时出现性能问题自动运行并清理一堆 -
告诉我!
威廉
【问题讨论】:
标签:
c#
garbage-collection
idisposable
【解决方案1】:
只有在需要清理 .Net 无法自动清理的非托管资源时,才应创建终结器。
如果您需要清理昂贵的托管或非托管资源,您应该实施IDisposable。昂贵并不意味着记忆; “昂贵”的托管资源是指对非托管资源(例如,文件、流、GDI+ 等)的封装
无法强制 GC 收集特定对象。
【解决方案2】:
这取决于您所说的“清理” -
如果您只是指释放托管内存,那么没有直接的方法可以为您的对象清理内存,只有您的对象。事实上,你不会想要这样做——GC 是高效的,并且试图“控制”它往往会弄乱它的启发式。这就是为什么直接调用GC.Collect 是个坏主意的部分原因。
如果您正在管理资源,例如原生资源,或其他必须发布的类似概念,那么IDisposable 是您的最佳选择。但是,您应该正确实现它,这与您的示例代码非常不同。有关正确实现的详细信息,您可以查看我的series on IDisposable。
【解决方案3】:
您不应该尝试“控制 GC”,除非在极少数情况下。几乎可以保证你不会遇到这种情况。永远。
IDisposable.Dispose() 与 GC 或析构函数并不真正(直接)相关。
-
Dispose() 用于清理托管资源,内存除外。它需要由您的代码显式调用。作为备份,一些类将调用放在析构函数中。但应该在此之前调用它。
- 析构函数用于清理非托管资源,通常还包括内存以外的资源。但您也应该在
Dispose() 中这样做。
- GC 会自动运行并很好地完成工作 - 它会清理对象使用的内存。您通常不应该对此大惊小怪。
【解决方案4】:
要调用CarInstance.Dispose(),它必须实现IDisposable,然后终结器应该调用Dispose(false),以便所有清理代码都在一个位置。
遵循此 question 中引用的 Finalize / Dispose 模式。
至于“清理语句”中应该包含什么内容,这应该包含清理任何非托管资源的代码。 .NET 无法自行清理的东西。
【解决方案5】:
这是我用于存储库的基类的代码 sn-p。它利用 IDisposable 接口来清理 Linq-to-SQL 上下文。
/// <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
}