4

我有一个作为其绑定源的UI控件。Enumerable但在我设置绑定源之前,我必须过滤我原来Enumerable的 . 我想为此目的使用LINQ :

control.BindingSource = from var item in enumerable.Cast<ItemType>()
                        where item.X == 1
                        select item;

这是一个 UI 挂起的问题,因为 enumerable 很慢(例如,如果实现类似yield return new Item(); Thread.Sleep(1000) ...)并且控制尝试在 UI 线程中执行查询。我试图通过结合使用 Task 和 async-await 来解决这个问题:

control.BindingSource = await Task.Factory.StartNew(() =>
                            (from var item in enumerable.Cast<ItemType>()
                             where item.X == 1
                             select item).ToArray());

现在 UI 不会挂起,但查询执行完成后结果立即可见。我通过在构造中使用ObservableCollectionand Enumeratorwith awaitbeside来解决这个问题:MoveNextwhile

var source = new ObservableCollection<object>();
control.BindingSource = source;

var enumerator = enumerable.GetEnumerator();
while (await Task.Factory.StartNew(() => enumerator.MoveNext()))
{
    var item = (ItemType)enumerator.Current;

    if (item.X == 1)
        source.Add(item);
}

我正在寻找至少允许使用 LINQ 的解决方案。有什么想法吗?

4

2 回答 2

2

不幸的是,async它不能很好地与 LINQ 配合使用。Rx 团队有一个“异步枚举器”实验过,但我相信它已经被放弃了。

基于任务的异步模式确实有一个标准的进度报告方法,您可以在此处使用。

private static void EvaluateItems(IEnumerable<ItemType> items, IProgress<ItemType> progress)
{
  if (progress == null)
    return;
  var query = from var item in items where item.X == 1 select item;
  foreach (var result in query)
    progress.Report(result);
}


var source = new ObservableCollection<object>();
control.BindingSource = source;
var progress = new Progress<ItemType>(item => source.Add(item));
await Task.Run(() => EvaluateItems(enumerable.Cast<ItemType>(), progress);

IProgress基于 - 的代码具有更大的关注点分离。该EvaluateItems方法只涉及枚举和过滤项目。它不必知道他们正在进入一个ObservableCollection或它正在 WPF 应用程序 ( Dispatcher) 中运行。所以它更便携和可测试。

于 2013-07-31T12:16:08.577 回答
1

试试这种方式:

var query = from var item in enumerable.Cast<ItemType>()
            where item.X == 1
            select item;

var source = new ObservableCollection<object>();
control.BindingSource = source;

Task.Factory.StartNew(
    () =>
    {
        foreach(var item in query)
        {
            Application.Current.Dispatcher.BeginInvoke(
                new Action(() => source.Add(item)));
        }
    },
    TaskCreationOptions.LongRunning);
于 2013-07-31T11:12:32.887 回答