[英]C# disposable objects
是否有一些关于如何处理IDisposable
对象序列的建议?
例如,我有一个构建IEnumerable<System.Drawing.Image>
序列的方法,在某些时候我需要手动处理这些对象,否则这可能会导致一些泄漏。
现在,有没有办法将Dispose()
调用绑定到垃圾回收器操作,因为我希望这些对象在不再可以从其他代码部分访问的时刻处理好?
** 或者你可以建议我采取其他方法吗? **
通常,这似乎是同样的问题,例如,在没有共享指针的非托管C++
,您可以拥有一个方法:
SomeObject* AllocateAndConstruct();
然后你不能确定何时处理它,如果你不使用代码合同或不在评论中陈述某些内容。
我猜一次性物体的情况大致相同,但我希望有一个合适的解决方案。
(来自这个问题)
现在,有没有办法将Dispose()调用绑定到垃圾回收器操作,因为我希望这些对象在不再可以从其他代码部分访问的时刻处理好?
当物体超出范围/范围时,GC不会立即发生; 这是不确定的。 当GC看到它时,没有必要做其他任何事情(终结者尚未处理),因为为时已晚。
那么,诀窍是知道你何时完成它,并自己调用Dispose()
。 在许多情况下using
实现了这一点。 例如,您可以编写一个实现IDisposable
的类并封装一组Image
- 并使用包装您对该封装对象的using
。 该Dispose()
的包装可能Dispose()
持有的所有图像。
即
using(var imageWrapper = GetImages()) {
foreach(var image in imageWrapper) {
...
}
// etc
} // assume imageWrapper is something you write, which disposes the child items
但是,如果要在UI上显示数据,这有点棘手。 那里没有捷径; 您必须跟踪每个图像的完成时间,或接受非确定性的最终确定。
如果要确定处理集合中的对象,则应在每个对象上调用Dispose
:
myImages.ToList().ForEach(image => image.Dispose());
如果您不这样做,并且如果您的对象无法访问,GC将最终运行并释放它们。
现在,如果您不想手动编写Dispose
调用代码,则可以创建一个实现IDisposable
的包装类,并通过using
语句使用它:
using (myImages.AsDisposable()) {
// ... process the images
}
这是所需的“基础设施”:
public class DisposableCollectionWrapper<D> : IDisposable
where D : IDisposable {
private readonly IEnumerable<D> _disposables;
public DisposableCollectionWrapper(IEnumerable<D> disposables) {
_disposables = disposables;
}
public void Dispose() {
if (_disposables == null) return;
foreach (var disposable in _disposables) {
disposable.Dispose();
}
}
}
public static class CollectionExtensions {
public static IDisposable AsDisposable<D>(this IEnumerable<D> self)
where D : IDisposable {
return new DisposableCollectionWrapper<D>(self);
}
}
另请注意,这与您在C ++中描述的情况不同。 在C ++中,如果不delete
对象,则会出现真正的内存泄漏。 在C#中,如果不处理对象,垃圾收集器最终将运行并清理它。
您应该以您不知道何时不再需要资源的方式设计系统。 在最糟糕的情况下,它们最终会在垃圾收集器到达时被处理掉,但IDisposable的意思是你可以提前释放重要的资源。
例如,当您使用它们的窗口关闭时,或者当您的工作单元完成对它们执行的任何操作时,您可以定义此“早期”。 但在某些时候,某些对象应该“拥有”这些资源,因此应该知道它们何时不再需要。
简洁的方法是创建自己的实现IDisposable的泛型集合类。 当这个集合类是Disposed()时,如果它实现了IDisposed,请询问每个元素,如果是,请设置它。
示例(如果您不了解IDisposable模式,请查看其他位置)
public class MyDisposableList<T> : List<T> : IDisposable
{
private bool disposed = false;
~MyDisposableList()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
}
protected void Dispose(bool disposing)
{
if (!disposed)
{
foreach (T myT in this)
{
IDisposable myDisposableT = myT as IDisposable;
if (myDisposableT != null)
{
myDisposableT.Dispose();
}
myT = null;
}
this.Clear();
this.TrimExcess();
disposed = true;
}
}
...
}
用法:
using (MyDisposableList<System.Drawing.Bitmap> myList = new ...)
{
// add bitmaps to the list (bitmaps are IDisposable)
// use the elements in the list
}
using语句的结束自动释放myList,因此myList中的所有bitMaps顺便说一下:如果你从文件中加载了位图而你忘了Dispose()你不知道何时可以删除该文件的位图。
您可以使用'using'块来确保IDisposable在块保留后立即处理。 编译器会将这些块封装到try-finally语句中,以确保在离开块时调用Dispose。
通过使用终结器,可以使GC调用Dispose方法来处理那些以某种方式“遗漏”的对象。 但是,实现终结器更加昂贵并且会降低垃圾收集效率 - 并且可能降低应用程序的整体性能。 因此,如果有可能,您应该尽量确保自己处理您的IDisposables; 确定性:
public class Context : IDisposable {
List<IDisposable> m_disposable = new List<IDisposable>();
public void AddDisposable(IDisposable disposable) {
m_disposable.Add(disposable);
}
public void Dispose() {
foreach (IDisposable disp in m_disposable)
disp.Dispose();
}
// the Context class is used that way:
static void Main(string[] args) {
using (Context context = new Context()) {
// create your images here, add each to the context
context.AddDisposable(image);
// add more objects here
} // <- leaving the scope will dispose the context
}
}
通过使用一些巧妙的设计,将对象添加到上下文的过程可以变得更加容易。 可以将上下文赋予创建方法或通过静态单例发布它。 这样,它也可用于子方法范围 - 无需传递对周围环境的引用。 通过利用该方案,甚至可以模拟像C ++中已知的人工析构函数。
简洁的方法是创建自己的实现IDisposable的泛型集合类。 当这个集合类是Disposed()时,如果它实现了IDisposed,请询问每个元素,如果是,请设置它。
示例(如果您不了解IDisposable模式,请查看其他位置)
public class MyDisposableList<T> : List<T> : IDisposable
{
private bool disposed = false;
~MyDisposableList()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
}
protected void Dispose(bool disposing)
{
if (!disposed)
{
foreach (T myT in this)
{
IDisposable myDisposableT = myT as IDisposable;
if (myDisposableT != null)
{
myDisposableT.Dispose();
}
myT = null;
}
disposed = true;
}
}
...
}
用法:
using (MyDisposableList<System.Drawing.Bitmap> myList = new ...)
{
// add bitmaps to the list (bitmaps are IDisposable)
// use the elements in the list
}
using语句的结束自动释放myList,因此myList中的所有bitMaps顺便说一下:如果你从文件中加载了位图而你忘了Dispose()你不知道何时可以删除该文件的位图。
你可以调用GC.Collect()
如果你真的要立即处理这些对象,但根据我的理解,由GC决定是否收集内存。
这反过来将为每个应该释放的对象调用Finalize()
方法。
请注意,如果集合超出范围,GC将最终收集图像使用的内存。
如果使用实现IDisposeable的集合,也可以使用using构造。 这将保证在集合超出范围时(或几乎在范围结束之后)准确处理对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.