优化IEnumerable扩展MaxBy
本文关键字:MaxBy 扩展 IEnumerable 优化 | 更新日期: 2023-09-27 18:00:23
我有一个IEnumerable扩展MaxBy,可以像一样使用
var longest = new [] {"cat", "dogs", "nit" }.MaxBy(x=>x.Count())
应该是
"dogs"
带有实现*强调文本*
public static T MaxBy<T,M>(this IEnumerable<T> This, Func<T,M> selector)
where M : IComparable
{
return This
.Skip(1)
.Aggregate
( new {t=This.First(), m = selector(This.First())}
, (a, t) =>
{
var m = selector(t);
if ( m.CompareTo(a.m) > 0)
{
return new { t, m };
}
else
{
return a;
}
}
, a => a.t);
}
它相当优雅,纯粹是功能性的,但我发现有问题。我正在使用匿名对象它们是引用类型并且需要垃圾收集。在旅行的最坏情况下长度为N的IEnumerable将进行N次内存分配,N个对象将需要垃圾收集
我可以编写使用外部可变累加器的代码,但从美学角度来说,我更喜欢坚持我的模式。
然而,我的担忧在现实中是个问题吗?.Net一代人垃圾收集器识别出这些对象的寿命很短,只有一个位于一段时间并优化掉正在发生的事情?还是我创建一个自定义值类型(结构)来保存我的累加器,而不是使用匿名对象。
**编辑**
这显然是一种非功能性的方式
public static T MaxBy<T,M>(this IEnumerable<T> This, Func<T,M> selector)
where M : IComparable
{
var t = This.First();
var max = selector(t);
foreach (var item in This.Skip(1))
{
var m = selector(item);
if ( m.CompareTo(max) > 0)
{
max = m;
t = item;
}
}
return t;
}
这个更好:
public static T MaxBy<T, M>(this IEnumerable<T> source, Func<T, M> selector)
where M : IComparable
{
return source.Aggregate((record, next) =>
Comparer<M>.Default.Compare(selector(next), selector(record)) > 0
? next
: record);
}
- 您没有那么多代码(更智能地使用
.Aggregate<>
) - 您避免使用匿名类型
- 使用
Comparer<M>.Default
可以避免在非常常见的情况下装箱,在这种情况下,M
是一个实际上是IComparable<M>
(而不仅仅是IComparble
)的值类型。在您的示例中,M
是int
,这适用!对于值类型和引用类型,如果M
真的实现了泛型IComparable<M>
,则可以绕过类型检查。但是,如果M
是一个只有非泛型IComparable
(约束where M : IComparable
)的"差"类型,那么一切都会起作用
当source
为空时,InvalidOperationException
的行为保持不变。