Overuse of using and Dispose () to handle scope not for releasing objects?

For convenience and security, I would like to use the using statement to place and release objects from / to the pool

 public class Resource : IDisposable { public void Dispose() { ResourcePool.ReleaseResource(this); } } public class ResourcePool { static Stack<Resource> pool = new Stack<Resource>(); public static Resource GetResource() { return pool.Pop(); } public static void ReleaseResource(Resource r) { pool.Push(r); } } 

and pool access for example

 using (Resource r = ResourcePool.GetResource()) { r.DoSomething(); } 

I found several topics for abuse of using and Dispose() to handle the scope, but they all include using (Blah b = _NEW_ Blah()) .
Here, the objects should not be freed after leaving the scope, but stored in the pool.
If the using statement simply expands to a simple try finally Dispose() , this should work fine, but is there something more going on behind the scenes, or is it likely that this will not work in future versions of .Net?

+6
source share
6 answers

This is not an abuse at all - it is a common C # idiom. For example, ADO.NET objects (connections, operators, query results) are usually enclosed in using blocks, although some of these objects will be released back to their pools inside Dispose methods:

 using (var conn = new SqlConnection(dbConnectionString)) { // conn is visible inside this scope ... } // conn gets released back to its connection pool 
+7
source

This is a valid way to use IDisposable .

In fact, this is how connection pooling in .NET DBConnection - wrapping a DBConnection object in a using statement to make sure the connection is closed and returns to the connection pool.

TransactionScope is another example for a class that uses the Dispose pattern to roll back incomplete transactions:

A call to the Dispose method marks the end of the transaction scope.

+2
source

If the using statement simply expands to a simple try finally Dispose (), this should work fine, but is there something more going on behind the scenes, or is it likely that this will not work in future versions of .Net?

This is true. Your code should work fine, and it is guaranteed that the specification continues to work the same. This is actually quite common (look at the connection pool in SQL, for a good example.)

The main problem with your code, as it is written, is that you can explicitly call ReleaseResource within using , it can cause the pool to receive resources that will be redirected more than once, since it is part of the public API.

+2
source

It looks like an abuse of IDisposable and a poor design decision for me. First, it makes objects that are stored in the pool aware of the pool. This is similar to creating a List type that forces objects in it to implement a specific interface or be derived from a specific class. Like a LinkedList class that forces data items to include Next and Previous pointers, which a list can use.

In addition, you have a pool that allocates a resource for you, but then the resource has a call to return itself to the pool. That seems ... weird.

I think the best alternative would be the following:

 var resource = ResourcePool.GetResource(); try { } finally { ResourcePool.FreeResource(resource); } 

This is a bit more code (try / finally, not using ), but a cleaner design. It frees the contained objects from the need to know about the container, and it more clearly shows that the pool controls the object.

+2
source

Your understanding of the using statement is correct ( try , finally , Dispose ). I do not foresee this change in the near future. So many things would break if that were to happen.

There is not necessarily something wrong with what you are planning. I have seen this before, where Dispose does not actually close the object, but rather puts it in some kind of state that is not "fully operational".

If this bothers you at all, you can implement it in such a way that it adheres to the usual Dispose implementation pattern. Just have a wrapper class that implements IDisposable and provides all the methods of the base class. When the shell is located, put the main object in the pool, not the shell. You can then consider that the shell is closed, although the thing that it wraps is not.

+1
source

What you do is similar to C ++ and RAII. And in C #, it's about as close as the C ++ / RAII idiom as you can get.

Eric Lippert, who knows something or something about C #, is categorically against using the IDispose operator and using C # RAII as an idiom. See His detailed answer here, Is it insulting to use IDisposable and "using" as a means to get "scope behavior". for exception safety? .

Part of the problem with the IDisposable used in this RAII method is that IDisposable has very strict requirements for proper use. Almost all of the C # code I've seen that uses IDisposable cannot correctly implement the template. Joe Duffy made a blog post detailing the right ways to implement the IDisposable http://joeduffyblog.com/2005/04/08/dg-update-dispose-finalization-and-resource-management/ template. Joe's information is much more detailed and extensive than is mentioned in MSDN. Joe also knows something about C #, and there were many very smart contributors who helped shape this document.

Simple things can be done to implement a minimal IDisposable template (for use, for example, in RAII), for example, to compact a class, and since there are no unmanaged resources that do not have a finalizer, and such. MSDN https://msdn.microsoft.com/en-us/library/system.objectdisposedexception%28v=vs.110%29.aspx is a good overview, but Joe's information contains all the details.

But one thing you can't get away with with IDisposable is its viral nature. Classes that hold on to an element that is IDisposable themselves should become IDisposable ... not a problem in the using(RAII raii = Pool.GetRAII()) script using(RAII raii = Pool.GetRAII()) , but something to keep in mind.

All that has been said, despite Eric’s position (from which I tend to agree with him on most of the rest) and the Joe 50 page essay on how to implement the IDisposable template correctly ... I use it as a C # RAII idiom.

Now, only if C # has 1) non-empty links (e.g. C ++ or D or SpeC #) and 2) deeply immutable data types (e.g. D or even F # [ , you can do F # C #, but this LOT of the template, and it is too difficult to do it right ... makes it easy hard and hard impossible ]) and 3) design by contract as part of the language itself (for example, D or Eiffel or Spe #, and not the abomination of C # Code Contracts). sigh Maybe C # 7.

0
source

Source: https://habr.com/ru/post/951245/


All Articles