是否有关于如何处理IDisposable
对象序列的建议?
例如,我有一个构建IEnumerable<System.Drawing.Image>
序列的方法
在某些时候,我需要手动处理这些对象,否则这可能会导致一些泄漏。
现在,有没有办法将Dispose()
调用绑定到垃圾收集器操作,因为我希望这些对象在其他代码部分不再可访问的时刻处理好?
** 或者你可以建议我采取其他方法吗? **
通常,这似乎是一个问题,例如,在没有共享指针的非托管C++
中,您可以使用方法:
SomeObject* AllocateAndConstruct();
然后你不能确定何时处理它,如果你不使用代码合同或者没有在评论中陈述某些内容。
我猜一次性物品的情况大致相同,但我希望有一个合适的解决方案。
答案 0 :(得分:6)
(来自问题)
现在,有没有办法绑定 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上显示数据,这有点棘手。那里没有捷径;您必须跟踪每个图像的完成时间,或接受非确定性的最终确定。
答案 1 :(得分:6)
如果您想确定地处理集合中的对象,则应在每个对象上调用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#中,如果你不处理你的对象,垃圾收集器最终会运行并清理它。
答案 2 :(得分:5)
在不再需要资源时,您应该以知道的方式设计系统。在最糟糕的情况下,它们最终会在垃圾收集器到达时被处理掉,但IDisposable的意思是你可以提前释放重要的资源。
这个“早先”由你来定义,例如,你可以在使用它们的窗口关闭时,或者当你的工作单元完成对它们进行的任何操作时释放它们。但在某些时候,某些对象应“拥有”这些资源,因此应该知道它们何时不再需要。
答案 3 :(得分:1)
您可以使用'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
}
}
通过使用一些聪明的设计,将对象添加到上下文的过程可能会变得更加容易。可以将上下文赋予创建方法或通过静态单例发布它。这样,它也可用于子方法范围 - 无需传递对周围环境的引用。通过利用该方案,甚至可以模拟像f.e.这样的人工析构函数。从C ++中知道。
答案 4 :(得分:0)
整洁的方法是创建自己的实现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()您不知道何时可以删除该文件的位图。
答案 5 :(得分:0)
整洁的方法是创建自己的实现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()位图,则您不知道何时可以删除该文件。
答案 6 :(得分:-2)
如果您真的要立即处理这些物品,可以致电GC.Collect()
,但根据我的理解,由GC来决定是否收集记忆。
这反过来会为每个应该释放的对象调用Finalize()
方法
请注意,如果集合超出范围,GC将最终收集图像使用的内存
如果使用实现IDisposeable的集合,也可以使用using构造。这将保证在集合超出范围时(或几乎在范围结束之后)准确处理对象。