更多林克获得。它有什么作用?

时间:2014-01-31 14:55:47

标签: c# morelinq

我正在检查Jon Skeet的MoreLinq,我对获取扩展源代码感到好奇

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;
            }
        }

根据我的理解,它会收到IEnumerable<IDisposable>并创建List<IDisposable>

我无法理解这里可能出现的问题。

任何人都可以向我解释,并可能提供一个示例,其中此扩展可能有用吗?

3 个答案:

答案 0 :(得分:15)

AddRange的调用会重复source。如果出于任何原因遇到异常,则先前获得的任何异常将被处置。考虑这个例子:

var filenames = new[] { "file1.xml", "file2.xml", "doesnotexist.xml" };
var disposables = filenames.Select(fn => File.OpenRead(fn));
var fileStreams = disposables.Acquire();

由于懒惰的评估,当您分配disposables时,不会抛出任何异常。但是,当AddRangeAquire的调用到达第三个元素(尝试打开"doesnotexist.xml")时,将抛出FileNotFoundException。发生这种情况时,Acquire将安全地处理以前的流。一个简单的ToList / ToArray会使前两个文件流保持打开状态。

从本质上讲,Acquire可以确保filenames中的 all 文件安全地打开,或者没有任何文件。

答案 1 :(得分:6)

假设您拥有逐个创建和返回一次性对象的代码:

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();

答案 2 :(得分:2)

请记住,使用LINQ获得的大多数IEnumerable集合都是以懒惰的方式进行评估,例如:你只需要配方如何生成列表。只有在遍历集合时才会实际执行代码,在这种情况下,代码在disposables.AddRange(source)中发生。如果此调用失败,那么您最终会得到应该处理的部分对象集合,其发生在此处:

            foreach (var disposable in disposables)
                disposable.Dispose();