我有一组名为 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/