2012-05-15 36 views
3

在我問Retrieve a list of filenames in folder and all subfolders quickly的問題和其他一些我發現,似乎搜索許多文件的方法是使用EnumerateFiles方法。如何在完成之前訪問DirectoryInfo.EnumerateFiles

的EnumerateFiles和GetFiles的方法的區別如下:當您使用 EnumerateFiles,你將返回整個集合之前可以開始列舉名稱 的集合;當您使用GetFiles時, 必須等待返回整個數組名稱,然後才能訪問數組。因此,當您使用多個文件和 目錄時,EnumerateFiles可以更高效。

這聽起來對我來說很好,我的搜索時間約爲10秒,所以當信息進入時我可以開始排列我的列表。但我無法弄清楚。當我運行EnumerateFiles方法時,應用程序凍結,直到它完成。我可以在後臺工作人員運行它,但同樣的事情會發生在該線程上。任何幫助?

DirectoryInfo dir = new DirectoryInfo(MainFolder); 
List<FileInfo> matches = new List<FileInfo>(dir.EnumerateFiles("*.docx",SearchOption.AllDirectories)); 

//This wont fire until after the entire collection is complete 
DoSoemthingWhileWaiting(); 

回答

10

您可以通過將其推入後臺任務做到這一點。

例如,你可以這樣做:

var fileTask = Task.Factory.StartNew(() => 
{ 
    DirectoryInfo dir = new DirectoryInfo(MainFolder); 
    return new List<FileInfo>(
      dir.EnumerateFiles("*.docx",SearchOption.AllDirectories) 
      .Take(200) // In previous question, you mentioned only wanting 200 items 
     ); 
}; 

// To process items: 
fileTask.ContinueWith(t => 
{ 
    List<FileInfo> files = t.Result; 

    // Use the results... 
    foreach(var file in files) 
    { 
     this.listBox.Add(file); // Whatever you want here... 
    } 
}, TaskScheduler.FromCurrentSynchronizationContext()); // Make sure this runs on the UI thread 

DoSomethingWhileWaiting(); 

您在留言中提到:

我想在列表中顯示出來。和完美的將它們發送到主界面,因爲他們進來

在這種情況下,你必須處理它們在後臺運行,並把它們添加到列表中,因爲他們進來是這樣的:。

Task.Factory.StartNew(() => 
{ 
    DirectoryInfo dir = new DirectoryInfo(MainFolder); 
    foreach(var tmp in dir.EnumerateFiles("*.docx",SearchOption.AllDirectories).Take(200)) 
    { 
     string file = tmp; // Handle closure issue 

     // You may want to do this in batches of >1 item... 
     this.BeginInvoke(new Action(() => 
     { 
      this.listBox.Add(file); 
     })); 
    } 
}); 
DoSomethingWhileWaiting(); 
+0

謝謝你在裏德晚些時候來。我本可以在5分鐘前使用它。這幾乎是我想要的,但我已經從我過去的問題和上面的答案中找到了它。非常感謝您花時間爲您提供完整的答案。 –

+0

@ K'Leg更快!=更好;)我想你可能需要的一切,包括在這裏不止一個選項... –

+0

裏德看起來我們在你的第二個答案中缺少一個右括號。我試圖將其添加到最後,但我收到錯誤。任何想法? –

7

凍結因爲你是把它在List<FileInfo>的構造消耗此枚舉中的應用。這就好像你在急切地調用舊的方法。因此,正確的方法是在後臺線程中運行它,然後不立即將結果傳遞給會消耗枚舉直到結束的東西,而是在迭代到達循環時開始迭代和添加項目。

我可以在後臺工作人員運行它,但同樣的事情會發生在該線程。

是的,在循環過程中,你會明顯地凍結後臺線程,但這就是後臺線程的意思:避免凍結主UI線程。只要確保在將項目傳遞給主線程時向他們顯示您正在使用與UI的正確同步。在WinForms中,這發生在Control.Invoke方法中。當然要小心,因爲在後臺線程和UI線程之間經常進行編組也會產生負面影響,讓您感覺應用程序凍結了。要解決這個問題,你可以在文件到達時傳遞文件。

下面是一個例子:

Task.Factory.StartNew(() => 
{ 
    var dir = new DirectoryInfo(MainFolder); 
    var files = dir.EnumerateFiles("*.docx", SearchOption.AllDirectories); 
    foreach (var file in files) 
    { 
     Action<string> del = f => listBox1.Items.Add((string)f); 
     BeginInvoke(del, file); 
    } 
}); 
+0

所以,如果我不能把它變成一個列表如何存儲和訪問它?我很抱歉,但我沒有得到這個呢? –

+0

你想用這些結果做什麼?在UI中的某個地方顯示它們?在ListBox或什麼?您不應將它們存儲在列表中。您應該直接將結果編組到主UI線程,以便在他們到達時進行顯示。所以這將取決於你需要怎樣處理結果。 –

+0

是的,我想將它們顯示在列表中。並且完美地將它們發送到主要ui中。我所缺少的是,如果後臺工作者在處理此事件時凍結,我怎樣才能將它們發送給主UI? –

4

你可以做

forach(var f in dir.EnumerateFiles("*.docx",SearchOption.AllDirectories)) 
    { 
//.. process file 
    } 
螺紋

,並處理完畢後觸發一個事件

+0

所以如果我馬上跑這個,那麼5秒後再運行一次,它們可能會有不同的結果? –

+0

我的意思是第一個文件會在所有枚舉完成之前出現。如果你想處理文件系統更改,你可以使用filesystemwatcher:http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.aspx – gabba

+0

哦,等我想我明白了。如果我每次運行它都會找到結果,它會觸發另一個foreach循環......在foreach中,我可以從後臺工作人員向UI發送更新...是正確的嗎? –

相關問題