2014-10-03 36 views
-4

爲了簡化代碼,這樣我就可以更好地理解,我想這個遞歸調用轉換成while循環:如何將此遞歸調用重寫爲循環?

function getMusic(IStorageFolder folder) { 
    int cnt = 0; 
    var folders = await folder.GetFoldersAsync(); 
    if (folders != null) 
     foreach (var fol in folders) 
      await getMusic(fol); 

    var files = await folder.GetFilesAsync(); 
    foreach (var file in files) 
    { 
     MusicProperties musicProperties = await file.Properties.GetMusicPropertiesAsync(); 
     source.Add(new Music("artist", "title", "album"); 
     cnt++; 
    } 
} 

在代碼中,sourceMusic類型的一個ObservableCollection(包含三個參數見source.Add...一行)。

但是,我一直沒有成功。這是我的嘗試,它似乎沒有填充source列表。

private async Task getMusic(IStorageFolder folder) 
{ 
    Stack<StorageFolder> fold = new Stack<StorageFolder>(); 
    int count = 0; int firstTen = 0; 
    var folders = await folder.GetFoldersAsync(); 

    foreach (var indvFolder in folders) 
     fold.Push(indvFolder); 

    while (count < fold.Count) 
    { 
     var fol = fold.Pop(); 
     if (firstTen > 9) 
      break; 
     var files = await fol.GetFilesAsync(); 
     foreach (var file in files) 
     { 
      MusicProperties musicProperties = await file.Properties.GetMusicPropertiesAsync(); 
      source.Add(new Music("artist", "title", "album")); 
      count++; 
     } 
     firstTen++; 
    } 
} 
+0

有你在調試代碼中獲得成功..你能理解foreach循環嗎? – MethodMan 2014-10-03 16:46:48

+0

我在代碼中的每一行之後都有一個'Debug.WriteLine',用於檢查變量的值。我已經將它們從這些代碼中取出來保持清潔。 – DemCodeLines 2014-10-03 16:48:25

+0

所以你有什麼問題/問題你知道如何使用調試器的快速觀察 – MethodMan 2014-10-03 16:49:35

回答

0

你試圖在一個函數中做的太多,這就是複雜的代碼。從概念上講,您在這裏做了幾件不同的事情,獲取樹中的所有文件夾,從所有文件夾中獲取所有文件,並從所有文件中獲取所有屬性。分解這些操作。

遍歷文件系統以獲取所有文件是一種操作,您已經非常沮喪。事實上,因爲像這樣的樹木將要走過的同樣的方式的時候,你甚至可以概括異步樹遍歷到結構無關的算法:

public static async Task<IEnumerable<T>> TraverseAsync<T>(
    this IEnumerable<T> source 
    , Func<T, Task<IEnumerable<T>>> childrenSelector) 
{ 
    var queue = new Queue<T>(source); 
    List<T> results = new List<T>(); 
    while (queue.Any()) 
    { 
     var next = queue.Dequeue(); 
     results.Add(next); 
     foreach (var child in await childrenSelector(next)) 
      queue.Enqueue(child); 
    } 
    return results; 
} 

利用這一點,你現在可以把你的啓動文件夾,遍歷圖形來獲取所有的文件夾,這些文件夾映射到的所有文件,然後將這些文件映射到所有的屬性:

private async Task<IEnumerable<Music>> getMusic(IStorageFolder rootFolder) 
{ 
    var folders = await new[] { rootFolder } 
     .TraverseAsync(folder => folder.GetFoldersAsync()); 
    var files = (await Task.WhenAll(
     folders.Select(folder => folder.GetFilesAsync()))) 
     .SelectMany(folder => folder); 
    var properties = (await Task.WhenAll(
     files.Select(file => file.GetMusicPropertiesAsync()))) 
     .SelectMany(property => property); 
    return properties.Select(prop => new Music()); 
} 

這是由一點點混亂,因爲需要把一切都異步。如果我們首先做出SelectMany的異步版本:

public static async Task<IEnumerable<TResult>> SelectManyAsync<TSource, TResult>(
    this Task<IEnumerable<TSource>> source, 
    Func<TSource, Task<IEnumerable<TResult>>> resultSelector) 
{ 
    var sourceSequence = await source; 
    var sequences = await Task.WhenAll(sourceSequence.Select(resultSelector)); 
    return sequences.SelectMany(x => x); 
} 

它簡化了代碼:

private async Task<IEnumerable<Music>> getMusic(IStorageFolder rootFolder) 
{ 
    var properties = await new[] { rootFolder } 
     .TraverseAsync(folder => folder.GetFoldersAsync()) 
     .SelectManyAsync(folder => folder.GetFilesAsync()) 
     .SelectManyAsync(file => file.GetMusicPropertiesAsync()); 
    return properties.Select(prop => new Music()); 
} 

這裏注意到的是,我們還要確保這一方法實際上返回的結果是有在任務本身而不是通過變更列表作爲副作用。這確保Task類將負責所有必需的跨線程同步問題,並確保在異步操作實際完成之前(這可能是您的問題所在)之前無法訪問結果集合。

2

fold.Pop()變化fold.Countfold.Count將不包含已處理的項目,因此count < fold.Count沒有意義。

相反,使用while (fold.Count > 0)

而且,這個東西應該是在循環中,因爲原來的代碼訪問的子文件夾的子文件夾:

foreach (var indvFolder in fol.GetFoldersAsync()) 
    fold.Push(indvFolder); 

的設置只是

fold.Push(folder);