2014-01-31 41 views
6

我檢查喬恩斯基特的MoreLinq,我開始好奇的獲取擴展的源代碼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>

我無法理解這裏可能出現的問題。

任何人都可以解釋給我,並可能提供一個例子,這個擴展可以是有用的嗎?

+1

我認爲Jon Skeet是最適合回答這個問題的原因很明顯。就示例而言,我們只是說我有一個關閉文件描述符的序列/數組/列表,我想打開它們並對它們做一些事情(我需要所有這些描述符)。如果有任何文件無法打開,我想自動關閉所有其他描述符。在這種情況下,「Acquire」就可以做到這一點。 –

+0

懶惰的枚舉可能會在迭代過程中拋出一個異常。在這種情況下,你應該「處理」所有早期的對象,這個函數就是這樣做的。 – CodesInChaos

+0

所有的答案都是完美的,我想將它們全部標記爲*接受*,但我只能選擇一個。看着答案讓我感覺像是「哦」 –

回答

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沒有將引發異常,因爲懶的評價。但是,AddRangeAquire到達第三個元素(它試圖打開"doesnotexist.xml")時,會投出FileNotFoundException。發生這種情況時,Acquire將安全處理先前的流。簡單的ToList/ToArray會使前兩個文件流保持打開狀態。

從本質上說,Acquire有沒有保證,要麼所有filenames文件被安全地打開,或者他們都不是。

2

請記住,您使用LINQ得到的大部分IEnumerable集合都是以懶惰的方式進行評估的,例如,你得到的只是配方如何生成列表。只有當您迭代集合時纔會實際執行代碼,在這種情況下發生在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(); 
相關問題