为了简化代码以便我能更好地理解,我试图将这个递归调用转换为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++;
}
}
在代码中,source
是类型Music
的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 :(得分:2)
fold.Pop()
更改fold.Count
。 fold.Count
不会包含已处理的商品,因此count < fold.Count
毫无意义。
相反,请使用while (fold.Count > 0)
此外,这些内容应该在循环内部,因为原始代码访问了子文件夹的子文件夹:
foreach (var indvFolder in fol.GetFoldersAsync())
fold.Push(indvFolder);
设置只是
fold.Push(folder);
答案 1 :(得分: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
类将负责所有必要的跨线程同步问题,并确保在异步操作实际完成之前无法访问结果集合(这可能是您的问题所在)。