我正在检查Jon Skeet的Morelinq,我对收购扩展源代码感到好奇
实现如下
/// <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;
}
}
从我的理解中,它收到了IEnumerable<IDisposable>
,并创建了List<IDisposable>
。
我无法掌握这里可能出错的一切。
任何人都可以向我解释一下,并可能提供一个示例,该扩展名有用?
AddRange
的调用在source
上迭代。如果出于任何原因遇到一个例外,则将处置以前获得的任何例外。考虑此示例:
var filenames = new[] { "file1.xml", "file2.xml", "doesnotexist.xml" };
var disposables = filenames.Select(fn => File.OpenRead(fn));
var fileStreams = disposables.Acquire();
由于懒惰的评估,分配disposables
时,将不例外。但是,当Aquire
内的AddRange
调用到达第三个元素(尝试打开"doesnotexist.xml"
)时,将抛出FileNotFoundException
。发生这种情况时,Acquire
将安全地处理先前的流。简单的ToList
/ToArray
将使前两个文件流保持打开。
本质上,Acquire
可以确保安全打开filenames
中的文件,或者没有一个。
假设您有代码创建并返回一次性对象一个:
public IEnumerable<FileStream> GetFiles()
{
yield return File.OpenRead("file1");
yield return File.OpenRead("file2"); // does not exist
yield return File.OpenRead("file3");
}
您需要获取所有一次性对象,但是如果在获取中有一个例外,那么已经产生的对象将保留在记忆中而不会被处置。因此,Acquire
要么获取所有流并返回它们,要么在失败后,它处理了所有已经获得的流并重新抛弃异常。
FileStream[] streams = GetFiles().Acquire();
请记住,使用LINQ获得的大多数IEnumerable
集合都以懒惰的方式评估,例如您只会得到食谱如何生成列表。仅当您迭代集合时,该代码才能实际执行,在这种情况下,这会发生在disposables.AddRange(source)
中。如果此呼叫失败,那么您最终会得到部分应处理的对象集合,这在这里发生:
foreach (var disposable in disposables)
disposable.Dispose();