C# 一次性对象

发布于 2024-11-03 12:22:53 字数 547 浏览 1 评论 0原文

关于如何处理 IDisposable 对象序列,是否有一些建议?

例如,我有一个构建 IEnumerable 序列的方法, 在某些时候,我需要手动处理这些对象,因为否则这可能会导致一些泄漏。

现在,有没有办法将 Dispose() 调用绑定到垃圾收集器操作,因为我希望这些对象在其他代码不再访问时立即被处置部分?

**或者也许你可以建议我一些其他方法? **


一般来说,这似乎是同样的问题,例如,在没有 的非托管 C++ 中>共享指针,您可以在其中拥有一个方法:

SomeObject* AllocateAndConstruct();

然后,如果您不使用代码契约或不在注释中说明某些内容,则无法确定何时处置它。

我想一次性物品的情况也差不多,但我希望有一个合适的解决方案。

Are there some advices about how I should deal with the IDisposable object sequences?

For example, I have a method that builds a IEnumerable<System.Drawing.Image> sequence and
at some point I would need to dispose that objects manually, because otherwise this might lead to some leaks.

Now, is there a way to bind the Dispose() call to garbage collector actions, because I want these objects disposed right in the moment they are no longer accessible from other code parts?

**Or maybe you could advice me some other approach? **


Generally, this seems to be the same problem as it comes, for example, in unmanaged C++ without shared pointers, where you can have a method:

SomeObject* AllocateAndConstruct();

and then you can't be sure when to dispose it, if you don't use code contracts or don't state something in the comments.

I guess the situation with disposable objects is pretty the same, but I hope there is an appropriate solution for this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

愿得七秒忆 2024-11-10 12:22:53

(来自问题)

现在有没有办法绑定
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 上显示数据,这会有点棘手。那里没有捷径;您必须跟踪完成每个图像的时间,或者接受非确定性的最终确定。

(from the question)

Now, is there a way to bind the
Dispose() call to garbage collector
actions, because I want these objects
disposed right in the moment they are
no longer accessible from other code
parts?

GC doesn't happen immediately when your object goes out of scope / reach; it is non-deterministic. By the time GC sees it, there is no point doing anything else (that isn't already handled by the finalizer), as it is too late.

The trick, then, is to know when you are done with it, and call Dispose() yourself. In many cases using achieves this. For example you could write a class that implements IDisposable and encapsulates a set of Images - and wrap your use of that encapsulating object with using. The Dispose() on the wrapper could Dispose() all the images held.

i.e.

using(var imageWrapper = GetImages()) {
    foreach(var image in imageWrapper) {
         ...
    }
    // etc
} // assume imageWrapper is something you write, which disposes the child items

however, this is a bit trickier if you are displaying the data on the UI. There is no shortcut there; you will have to track when you are done with each image, or accept non-deterministic finalization.

请止步禁区 2024-11-10 12:22:53

如果您想明确地处置集合中的对象,则应该对每个对象调用 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++ 中,如果您不删除对象,就会出现真正的内存泄漏。在 C# 中,如果您不释放对象,垃圾收集器最终将运行并清理它。

If you want to determiniscally dispose of the objects in the collection, you should call Dispose on each:

myImages.ToList().ForEach(image => image.Dispose());

If you don't do this, and if your objects become unreachable, the GC will eventually run and release them.

Now, if you don't want to manually code the Dispose calls, you can create a wrapper class that implements IDisposable and use it through a using statement:

using (myImages.AsDisposable()) { 
  // ... process the images
}

This is the needed "infrastructure":

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);
  }

}

Also notice that this is not the same as the situation you described with C++. In C++, if you don't delete your object, you have a genuine memory leak. In C#, if you don't dispose of your object, the garbage collector will eventually run and clean it up.

醉城メ夜风 2024-11-10 12:22:53

您应该以一种您知道何时不再需要资源的方式来设计系统。在最坏的情况下,当垃圾收集器到达它们时,它们最终将被处置,但 IDisposable 的要点是您可以更早地释放重要资源。

这个“更早”由您来定义,例如,您可以在使用它们的窗口关闭时,或者当您的工作单元完成对它们执行的任何操作时释放它们。但在某些时候,某个对象应该“拥有”这些资源,因此应该知道何时不再需要它们。

You should design your system in a way that you know when the resources are no longer needed. In the worst case, they'll be eventually disposed when the garbage collector gets to it, but the point of IDisposable is that you can release important resources earlier.

This "earlier" is up to you to define, for example, you can release them when the window that's using them closes, or when your unit of work finishes doing whatever operations on them. But at some point, some object should "own" these resources, and therefore should know when they're no longer needed.

征棹 2024-11-10 12:22:53

您可以使用“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++ 中已知的人工析构函数功能。

You can use the 'using' block, to make sure, the IDisposable is disposed as soon the block is left. The compiler does encapsulate such blocks into try - finally statements in order to make sure, Dispose is called in any case when leaving the block.

By using a finalizer, one can make the GC call the Dispose method for those objects which where "missed" somehow. However, implementing a finalizer is more expensive and decreases the garbage collection efficiency - and possibly the overall performance of your application. So if any possible, you should try to make sure to dispose your IDisposables on your own; deterministically:

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
    }
}

By using some clever design, the process of adding objects to the context may can get even more easier. One might give the context to the creation method or publish it via a static singleton. That way, it would be available for child method scopes as well - without having to pass a reference to the contex around. By utilizing that scheme it is even possible, to simulate an artificial destructor functionality like f.e. known from C++.

护你周全 2024-11-10 12:22:53

简洁的方法是创建您自己的实现 IDisposable 的通用集合类。当这个集合类被 Dispose() 时,询问每个元素是否实现了 IDispose,如果是,则将其处理掉。

示例(如果您不知道 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 中的所有位图
顺便说一句:如果您从文件加载位图并且忘记 Dispose() 该位图,您不知道何时可以删除该文件。

The neat method would be to create your own generic collection class that implements IDisposable. When this collection class is Disposed() ask for each element if it implements IDisposed, and if so Dispose it.

Example (look elsewhere if you don't know about the IDisposable pattern)

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;
        }
    }
    ...
}

usage:

using (MyDisposableList<System.Drawing.Bitmap> myList = new ...)
{
    // add bitmaps to the list (bitmaps are IDisposable)
    // use the elements in the list
}

The end of the using statement automatically Disposes myList, and thus all bitMaps in myList
By the way: if you loaded the bitmap from a file and you forgot to Dispose() the bitmap you don't know when you can delete that file.

沐歌 2024-11-10 12:22:53

如果您确实要立即处置这些对象,您可以调用 GC.Collect() ,但根据我的理解,由 GC 决定是否收集内存。
这反过来将为每个应释放的对象调用 Finalize() 方法。
请注意,如果收集超出范围,GC 最终将收集图像使用的内存。
如果您使用实现 IDisposeable 的集合,则还可以使用 using 构造。这将保证当集合超出范围时(或几乎在范围结束后),对象将被准确处置。

You can call GC.Collect() if you really was to dispose those objects right away but to my understanding it is up to the GC to decide whether to collect the memory.
This in turn will call the Finalize() method for each object that should be released.
Note that if the collection goes out of scope the GC will eventually collect the memory used by the images.
You can also use the using construct if you use a collection that implements IDisposeable. That will guarantee that the objects will be disposed exactly when the collection goes out of scope (or nearly after the end of the scope).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文