Ensures that a sequence of disposable objects, those implementing
IDisposable
), are all acquired successfully. If the
acquisition of any one fails then those successfully acquired till that
point are disposed.
Following answer by Sergey Berezovskiy to the StackOverflow question, "MoreLinq Acquire. What does it do?", offers a great explanation:
Assume you have code which creates and returns disposable objects one by one:
public IEnumerable<FileStream> GetFiles() { yield return File.OpenRead("file1"); yield return File.OpenRead("file2"); // does not exist yield return File.OpenRead("file3"); }You need to get all of the disposable objects, but if in the middle of acquisition there is an exception, then the objects which were already yielded will stay in memory and not disposed. So,
Acquire
either acquires all streams and returns them, or, upon failing, it disposes all already acquired streams and rethrows the exception.var streams = GetFiles().Acquire();
The following example shows a function lazily yielding resources (objects
implementing IDisposable
) that are later acquired and disposed in a loop.
static IEnumerable<IDisposable> GetResources()
{
WriteLine("Yielding resource #1");
yield return Disposable(() => WriteLine("Resource #1 disposed"));
WriteLine("Yielding resource #2");
yield return Disposable(() => WriteLine("Resource #2 disposed"));
WriteLine("Yielding resource #3");
yield return Disposable(() => WriteLine("Resource #3 disposed"));
}
var i = 0;
foreach (var r in GetResources().Acquire())
{
i++;
using (r)
WriteLine($"Disposing resource #{i}");
}
The GetResources
function uses Disposable
from the Delegating library to
create ad-hoc implementations. Running the example will produce the output:
Yielding resource #1
Yielding resource #2
Yielding resource #3
Disposing resource #1
Resource #1 disposed
Disposing resource #2
Resource #2 disposed
Disposing resource #3
Resource #3 disposed
Note how all the resources were acquired eagerly at the start of the loop.
Suppose now GetResources
is modified to simulate an error in the generation
of the third resource:
static IEnumerable<IDisposable> GetResources()
{
WriteLine("Yielding resource #1");
yield return Disposable(() => WriteLine("Resource #1 disposed"));
WriteLine("Yielding resource #2");
yield return Disposable(() => WriteLine("Resource #2 disposed"));
WriteLine("Yielding resource #3");
throw new ApplicationException(); // Oops!
// CS0162: Unreachable code detected
yield return Disposable(() => WriteLine("Resource #3 disposed"));
}
try
{
var i = 0;
foreach (var r in GetResources().Acquire())
{
i++;
using (r)
WriteLine($"Disposing resource #{i}");
}
}
catch (Exception e)
{
WriteLine("ERROR! " + e.Message);
}
The ouput this time will read:
Yielding resource #1
Yielding resource #2
Yielding resource #3
Resource #1 disposed
Resource #2 disposed
ERROR! Error in the application.
Since not all resouces could be acquired successfully, because an error occurs
when the third resource is about to be yielded, Acquire
disposes the first
two resources that were acquired successfully up to when the error occurs and
propagates up the stack. Since the loop is never entered, it would not have
had a chance to dispose the resources. Without Acquire
, the resources would
have leaked.
For more details, see the documentation.
✏ Edit this page if you see a typo or wish to contribute an improvement. Alternatively, you can also report an issue you see.