I was inspecting the Jon Skeet's MoreLinq and I became curious about the acquire extension source code
The implementation is as follows
/// <summary>
/// Ensures that a source sequence of <see cref="IDisposable"/>
/// objects are all acquired successfully. If the acquisition of any
/// one <see cref="IDisposable"/> fails then those successfully
/// acquired till that point are disposed.
/// </summary>
/// <typeparam name="TSource">Type of elements in <paramref name="source"/> sequence.</typeparam>
/// <param name="source">Source sequence of <see cref="IDisposable"/> objects.</param>
/// <returns>
/// Returns an array of all the acquired <see cref="IDisposable"/>
/// object and in source order.
/// </returns>
/// <remarks>
/// This operator executes immediately.
/// </remarks>
public static TSource[] Acquire<TSource>(this IEnumerable<TSource> source)
where TSource : IDisposable
{
if (source == null) throw new ArgumentNullException("source");
var disposables = new List<TSource>();
try
{
disposables.AddRange(source);
return disposables.ToArray();
}
catch
{
foreach (var disposable in disposables)
disposable.Dispose();
throw;
}
}
From my understanding it receives a IEnumerable<IDisposable>
and it creates a List<IDisposable>
.
I can't grasp whatever can go wrong here.
Can anyone explain it to me, and possibly provide an example where this extension can be useful?
The call to AddRange
iterates over source
. If for any reason it encounters an exception, any that had previously been acquired will be disposed. Consider this example:
var filenames = new[] { "file1.xml", "file2.xml", "doesnotexist.xml" };
var disposables = filenames.Select(fn => File.OpenRead(fn));
var fileStreams = disposables.Acquire();
No exception will be thrown when you're assigning disposables
, because of lazy evaluation. However, when the call to AddRange
inside Aquire
reaches the third element (where it tries to open "doesnotexist.xml"
), a FileNotFoundException
will be thrown. When this happens, Acquire
will safely dispose the previous streams. A simple ToList
/ ToArray
would leave the first two file streams open.
In essence, Acquire
is there to ensure that either all the files in filenames
is safely opened, or none of them are.