在C#3.5中,只需一两行即可从集合中返回最合适的物品
下面是一些我一生中基本上写过数千次的示例代码:在C#3.5中,只需一两行即可从集合中返回最合适的物品,c#,linq,max,C#,Linq,Max,下面是一些我一生中基本上写过数千次的示例代码: // find bestest thingy Thing bestThing; float bestGoodness = FLOAT_MIN; foreach( Thing x in arrayOfThings ) { float goodness = somefunction( x.property, localvariable ); if( goodness > bestGoodness ) { bestGoodnes
// find bestest thingy
Thing bestThing;
float bestGoodness = FLOAT_MIN;
foreach( Thing x in arrayOfThings )
{
float goodness = somefunction( x.property, localvariable );
if( goodness > bestGoodness )
{
bestGoodness = goodness;
bestThing = x;
}
}
return bestThing;
在我看来,C#应该已经有了某种东西,可以在一行中实现这一点。比如:
return arrayOfThings.Max( delegate(x)
{ return somefunction( x.property, localvariable ); });
var sortedByGoodness = from x in arrayOfThings
orderby somefunction( x.property, localvariable ) ascending
select x;
return x.first;
但这并不返回返回拟合优度值的对象(或对象的索引,这很好)
所以可能是这样的:
return arrayOfThings.Max( delegate(x)
{ return somefunction( x.property, localvariable ); });
var sortedByGoodness = from x in arrayOfThings
orderby somefunction( x.property, localvariable ) ascending
select x;
return x.first;
但这样做需要整个阵列,可能太慢了
这是否存在?这是您可以使用System.Linq执行的操作:
var value = arrayOfThings
.OrderByDescending(x => somefunction(x.property, localvariable))
.First();
如果数组可以为空,请使用.FirstOrDefault()代码>以避免异常
您确实不知道这是如何在内部实现的,因此无法保证这将对整个数组进行排序以获得第一个元素。例如,如果是LINQtoSQL,服务器将收到一个包含排序和条件的查询。它不会获取数组,然后对其排序,然后获取第一个元素
事实上,除非您不先调用,否则不会计算查询的第一部分。我的意思是这不是两步评估,而是一步评估
var sortedValues =arrayOfThings
.OrderByDescending(x => somefunction(x.property, localvariable));
// values isn't still evaluated
var value = sortedvalues.First();
// the whole expression is evaluated at this point.
我认为在标准LINQ中,如果不对enuerable进行排序(在一般情况下这很慢),这是不可能的,但是您可以使用MoreLinq库中的MaxBy()
方法来实现这一点。我总是在我的项目中包含这个库,因为它非常有用
(代码实际上看起来非常类似于您所拥有的,但是是泛化的。)我将实现IComparable
,只需使用arrayOfThings.Max()
示例如下:
我认为这是最干净的方法,IComparable可能在其他地方也有用
更新
还有一个重载的Max
方法,它采用投影函数,因此您可以提供不同的逻辑来获取身高、年龄等
我按照注释中列出的链接Porges,在中运行以下代码,并验证两个LINQ表达式都返回了正确答案
void Main()
{
var things = new Thing [] {
new Thing { Value = 100 },
new Thing { Value = 22 },
new Thing { Value = 10 },
new Thing { Value = 303 },
new Thing { Value = 223}
};
var query1 = (from t in things
orderby GetGoodness(t) descending
select t).First();
var query2 = things.Aggregate((curMax, x) =>
(curMax == null || (GetGoodness(x) > GetGoodness(curMax)) ? x : curMax));
}
int GetGoodness(Thing thing)
{
return thing.Value * 2;
}
public class Thing
{
public int Value {get; set;}
}
来自LinqPad的结果
通过使用Aggregate
,您实际上可以在不使用普通LINQ排序的情况下完成这项工作,但除了最简单的情况外,它最终都非常难看。最好使用某种MaxBy
扩展方法。但是如果没有一种真正的方法来比较事情呢?例如,如果对象是人,则可以实现IComparable来超越他们的高度。但是,如果将来你想比较年龄会发生什么呢?同样,这与OP中的Max()有相同的问题;它返回的是值,而不是对象或对象的索引。@Porges感谢您指出链接。很高兴知道:)您不能在Linq2SQL中按任意函数排序-它必须是一个可以轻松转换为SQL ORDER BY子句的表达式。因此,我认为可以安全地假设我们将这个问题的范围限制在LINQ2对象上,因此枚举的排序可能是一个问题