c# - 如何在 C# 中报告对 .Distinct() 的长调用的进度

标签 c# .net linq distinct progress

我有一组名为 AnalysisResult 的自定义对象。该数组可以包含数十万个对象;而且,有时我只需要该数组的 Distinct() 元素。因此,我编写了一个名为 AnalysisResultDistinctItemComparer 的项目比较器类,并像这样调用:

public static AnalysisResult[] GetDistinct(AnalysisResult[] results)
{
    return results.Distinct(new AnalysisResultDistinctItemComparer()).ToArray();
}

我的问题是,当数组特别大(超过 200,000 个对象)时,此调用可能需要很长时间(大约几分钟)。

我目前在后台工作程序中调用该方法并显示一个旋转的 gif 以提醒用户该方法正在执行并且应用程序尚未卡住。这一切都很好,但它不会向用户提供当前进度的任何指示。

我确实需要能够向用户指示此操作的当前进度;但是,我一直无法想出一个好的方法。我正在玩这样的事情:

public static AnalysisResult[] GetDistinct(AnalysisResult[] results)
{
    var query = results.Distinct(new AnalysisResultDistinctItemComparer());

    List<AnalysisResult> retVal = new List<AnalysisResult>();
    foreach(AnalysisResult ar in query)
    {
        // Show progress here
        retVal.Add(ar);
    }

    return retVal.ToArray();
}

但问题是我无法知道我的实际进度是多少。想法?有什么建议吗?

最佳答案

不要在方法的末尾调用 ToArray(),只需使用 yield return。所以这样做:

public static IEnumerable<AnalysisResult> Distinct(AnalysisResult[] results)
{
    var query = results.Distinct(new AnalysisResultDistinctItemComparer());

    foreach(AnalysisResult ar in query)
    {
        // Use yield return here, so that the iteration remains lazy.
        yield return ar;
    }
}

基本上,yield return 做了一些编译器魔术来确保迭代保持惰性,因此您不必等待创建一个完整的新集合再返回给调用者。相反,在计算每个项目时,您立即将该项目返回给消费者(然后消费者可以执行更新逻辑——如有必要,针对每个项目)。您也可以在 GetDistinct 方法中使用相同的技术。

Jon Skeet 有一个看起来像这样的实现(LINQ's Distinct() on a particular property):

public static IEnumerable<TSource> DistinctBy<TSource, TKey>
    (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
    HashSet<TKey> seenKeys = new HashSet<TKey>();
    foreach (TSource element in source)
    {
        if (seenKeys.Add(keySelector(element)))
        {
            yield return element;
        }
    }
}

注意这里他使用了一个HashSet,它被构建为不允许重复。只需检查该项目是否已添加,如果没有,则将其退回。

总而言之,请记住这是一道算法和数据结构类型的问题。做这样的事情会容易得多:

Dictionary<Key, Value> distinctItems = new Dictionary<Key, Value>(); 

foreach (var item in nonDistinctSetOfItems) {
    if (distinctItems.ConatainsKey(item.KeyProperty) == false) {
        distinctItems.Add(item.KeyProperty, item);
    }
}

... = distinctItems.Values // This would contain only the distinct items.

也就是说,符号表/字典 就是为这类问题而构建的——将条目与唯一键相关联。如果以这种方式存储数据,就会大大简化问题。不要忽视简单的解决方案!

关于c# - 如何在 C# 中报告对 .Distinct() 的长调用的进度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18253448/

相关文章:

c# - 如何只发布需要的依赖?

C# linq groupby 包括 json

c# - 当对象为空且属性为整数时如何使用 OrderBy Linq

c# - 模块化 C# Compact Framework 2.0 应用程序

c# - 在 C# 中以无序序列重复和计数循环

C# - Console.WriteLine() 不显示第二个参数

c# - 使用 C# 和 Linq 以 1000 组为一组处理文档的 46,000 行

c# - LINQ Expression API 不提供创建变量的方法吗?

c# - DNN 7+应该如何使用LoggerSource?

c# - 如何在混合应用程序中获取有关缓冲区溢出异常的信息?