2014-01-31 3 views
6

я осматривал MoreLinq на Джона тарелочкам, и я стал любопытным о расширении приобретают исходный кодПодробнееLinq Acquire. Что оно делает?

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>.

Я не могу понять, что может пойти не так.

Может ли кто-нибудь объяснить это мне и, возможно, предоставить пример, где это расширение может быть полезно?

+1

Я думаю, что Джон Скит был бы наиболее подходящим для ответа на этот вопрос по понятным причинам. Что касается примеров, давайте просто скажем, что у меня есть последовательность/массив/список закрытых дескрипторов файлов, и я хотел бы открыть их все и сделать с ними что-то (и мне все они нужны). Если какой-либо из файлов не открывается, я хотел бы закрыть все остальные дескрипторы автоматически. В этой ситуации 'Acquire' будет делать именно это. –

+0

Ленивый перечислимый может исключить исключение на полпути через итерацию. В этом случае вы должны «Утилизировать» все более ранние объекты, которые выполняет эта функция. – CodesInChaos

+0

Все ответы идеальны, и я хотел бы отметить все их как * принятых *, но мне нужно было выбрать только один. Глядя на ответы, я почувствовал, как «D'oh» –

ответ

10

Звонок AddRange итерации свыше source. Если по какой-либо причине он встречает исключение, любое, которое ранее было приобретено, будет удалено. Рассмотрим следующий пример:

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

Исключение не будет сгенерировано, когда вы присваиваете disposables, из-за ленивых оценки. Однако, когда звонок AddRange внутри Aquire достигает третьего элемента (там, где он пытается открыть "doesnotexist.xml"), будет выброшен FileNotFoundException. Когда это произойдет, Acquire будет безопасно удалять предыдущие потоки. Простой ToList/ToArray оставил бы первые два потока файлов открытыми.

В сущности, Acquire есть для того, чтобы либо все файлы в filenames не безопасно открыт, или ни один из них.

2

Помните, что большинство коллекций IEnumerable, которые вы используете с помощью LINQ, оцениваются ленивым способом, например. вы получаете только рецепт создания списка. Код фактически выполняется только тогда, когда вы перебираете коллекцию, которая в этом случае происходит в disposables.AddRange(source). Если этот вызов не удается, то вы в конечном итоге с частичной коллекцией объектов, которые должны быть утилизированы, что происходит здесь:

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

Предположит у вас есть код, который создает и возвращает одноразовые предметы по одному:

public IEnumerable<FileStream> GetFiles() 
{ 
    yield return File.OpenRead("file1"); 
    yield return File.OpenRead("file2"); // not exist 
    yield return File.OpenRead("file3"); 
} 

Вам нужно получить все эти одноразовые объекты, но если в середине приобретения у вас будет исключение, то объекты, которые уже дали, останутся в памяти, а не в распоряжении. Таким образом, Acquire либо приобретают все потоки, либо возвращают их, либо располагают все уже полученные потоки и бросают, если происходит исключение, пока весь поток не будет получен.

FielStream[] streams = GetFiles().Acquire(); 
Смежные вопросы