C # Single Use Objects

Are there any tips on how I should work with sequences of IDisposable objects?

For example, I have a method that builds the sequence IEnumerable<System.Drawing.Image> and at some point I will need to destroy these objects manually, because otherwise it can lead to some leaks.

Now there is a way to bind the Dispose() call to the garbage collector's actions, because I want these objects to be located right at the moment when they are no longer available from other parts of the code?

** Or maybe you could advise me on a different approach? **


Generally, this is apparently the same problem as in the case of unmanaged C++ without common pointers, where you can have a method:

 SomeObject* AllocateAndConstruct(); 

and then you canโ€™t be sure when to remove it, if you do not use code contracts or indicate anything in the comments.

I assume that the situation with disposable objects is almost the same, but I hope that there is an appropriate solution for this.

+7
source share
7 answers

You must design your system so that you know when resources are no longer needed. In the worst case, they will eventually be deleted when the garbage collector gets to it, but the point of IDisposable is that you can free up important resources sooner.

This "earlier" is up to you to determine, for example, you can free them when the window that uses them closes or when your unit of work completes any operations on them. But at some point, some object must โ€œownโ€ these resources, and therefore must know when they are no longer needed.

+5
source

(from the question)

Now, is there a way to bind Dispose () to an action garbage collector call because I want these objects to be located right at the moment when they are no longer available from another part code?

GC does not happen immediately when your object goes beyond the scope / scope; it is not deterministic. By the time the GC sees this, it makes no sense to do anything else (this is no longer being handled by the finalizer), since it's too late.

Thus, the trick should know when you are done with it, and call Dispose() yourself. In many cases, using achieves this. For example, you can write a class that implements IDisposable and encapsulate the Image set - and end using this encapsulating object with using . Dispose() on the wrapper could Dispose() save all the images.

i.e.

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

However, it is a bit more complicated if you display data in the user interface. There is no shortcut; You will need to track when you are finished with each image, or accept the final finalization.

+6
source

If you want to deterministically manage objects in a collection, you should call Dispose for each:

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

If you do not, and if your objects become inaccessible, the GC will eventually launch and release them.

Now, if you do not want to manually code Dispose calls, you can create a wrapper class that implements IDisposable and use it in the using statement:

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

This is the necessary "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 note that this is not the same as the situation described in C ++. In C ++, if you do not delete your object, you have a real memory leak. In C #, if you do not destroy your object, the garbage collector eventually starts and clears it.

+6
source

You can use the using block to make sure that IDisposable is removed as soon as the block remains. The compiler encapsulates such blocks in a try-finally statement to ensure that Dispose is invoked anyway when the block exits.

Using the finalizer, you can make a GC call using the Dispose method for those objects that are "missed" somewhere. However, implementing a finalizer is more expensive and reduces garbage collection efficiency - and possibly the overall performance of your application. Therefore, if possible, you should try to independently implement your IDisposables; 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 } } 

Using some kind of smart design, the process of adding objects to the context can be even easier. You can give context to the creation method or publish it through a static singleton. Thus, it will also be available for child methods - without having to pass a link to the context around. Using this scheme, it is even possible to imitate the functionality of an artificial destructor, for example fe known from C ++.

+1
source

The best method would be to create your own collection class that implements IDisposable. When this collection class Disposed () asks for each element if it implements IDisposed and if it Dispose it.

Example (see elsewhere if you do not know about the IDisposable template)

 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:

 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 Dispose myList, and therefore all the bits in myList By the way: if you loaded a bitmap from a file and you forgot Dispose () the bitmap that you do not know when you can delete this file.

0
source

The best method would be to create your own collection class that implements IDisposable. When this collection class Disposed () asks for each element if it implements IDisposed and if it Dispose it.

Example (see elsewhere if you do not know about the IDisposable template)

 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:

 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 Dispose myList, and therefore all the bits in myList By the way: if you loaded a bitmap from a file and you forgot Dispose () the bitmap that you do not know when you can delete this file.

0
source

You can call GC.Collect() if you really should have deleted these objects immediately, but as far as I know, before GC you need to decide whether to collect memory.
This, in turn, will call the Finalize() method for each object that needs to 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 are using a collection that implements IDisposeable. This ensures that objects will be positioned exactly when the collection goes beyond (or almost after the end of the area).

-2
source

All Articles