我正在尝试添加通用的TopN IEnumerable<T>
扩展程序。
如果参数为正,那么它与Take()相同,但如果它是负数,那么它应该与Take()相同,但后来立即产生与Take()中最后一个值匹配的连续值。 (与SQL TOP n WITH TIES相同)
这是我目前的代码: -
public static class Test
{
public static IEnumerable<TSource> TopN<TSource>(this IEnumerable<TSource> source, int topN)
{
return TopN(source, topN, (v1, v2) => v1.Equals(v2));
}
public static IEnumerable<TSource> TopN<TSource>(this IEnumerable<TSource> source, int topN, Func<TSource, TSource, bool> comparer)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (comparer == null) throw new ArgumentNullException(nameof(comparer));
return topN >= 0
? source.Take(topN)
: TopNWithTiesIterator(source, -topN, comparer);
}
static IEnumerable<TSource> TopNWithTiesIterator<TSource>(this IEnumerable<TSource> source, int topN, Func<TSource, TSource, bool> comparer)
{
var lastItem = default(TSource);
foreach (var item in source)
{
if (topN-- > 0 || comparer(item, lastItem))
{
lastItem = item;
yield return item;
}
else
{
yield break;
}
}
}
}
以下是我尝试的实际使用情况和其他一些快速测试的示例:
if (TopN != 0)
{
var values = new[] { 1, 2, 2, 3 };
Debug.Assert(!values.TopN(0).Any());
Debug.Assert(!values.TopN(0, (v1, v2) => v1 == v2).Any());
Debug.Assert(values.TopN(1, (v1, v2) => v1 == v2).Count() == 1);
Debug.Assert(values.TopN(-1, (v1, v2) => v1 == v2).Count() == 1);
Debug.Assert(values.TopN(2, (v1, v2) => v1 == v2).Count() == 2);
Debug.Assert(values.TopN(-2, (v1, v2) => v1 == v2).Count() == 3);
Debug.Assert(values.TopN(2).Count() == 2);
Debug.Assert(values.TopN(-2).Count() == 3);
// This is how I really want to use it
summaries = summaries.TopN(TopN, (v1, v2) => v1.ClientValue + v1.AdviserValue == v2.ClientValue + v2.AdviserValue);
}
我的问题是使用Func<TSource, TSource, bool>
作为比较器是否正确。
我应该使用IEqualityComparer<T>
还是IEquatable<<T>
还是别的什么?
答案 0 :(得分:1)
默认和预期的行为应该是:
如果未提供IEqualityComparer<T>
,您的方法应检查TSource
是否实施IEquatable<T>
并使用IEquatable<T>.Equals(T)
。否则,它应该使用Object.Equals
。
如果提供了IEqualityComparer<T>
,请使用它。
如果提供谓词来模仿IEqualityComparer<T>
行为,则使用谓词代替IEqualityComparer<T>
。
答案 1 :(得分:0)
Func<TSource, TSource, bool>
和IEqualityComparer<T>
之间无法选择。您在Equals
使用默认对象的(v1, v2) => v1.Equals(v2)
比较器。
所以这取决于您是否希望通过Func<>
或直接作为可选方法参数灵活地提供自定义比较器。