C# 根据某种标准,从集合中选择最大项
我是.NET3.5的新手。 我收集了一些物品:C# 根据某种标准,从集合中选择最大项,c#,.net-3.5,extension-methods,C#,.net 3.5,Extension Methods,我是.NET3.5的新手。 我收集了一些物品: IList<Model> models; 我想得到元素,它的名称长度最长。 我试过了 string maxItem=models.Max(model=>model.Name.Length); 但是它当然返回最大长度(我需要一个模型对象)。我知道有一种方法可以使用扩展方法来实现这一点,但我不知道如何实现。不幸的是,没有一种内置的方法来实现这一点-但是编写一个扩展方法来实现这一点非常容易 事实上。。。注意,其中一条评论中有一个更好的实现
IList<Model> models;
我想得到元素,它的名称长度最长。
我试过了
string maxItem=models.Max(model=>model.Name.Length);
但是它当然返回最大长度(我需要一个
模型对象)。我知道有一种方法可以使用扩展方法来实现这一点,但我不知道如何实现。不幸的是,没有一种内置的方法来实现这一点-但是编写一个扩展方法来实现这一点非常容易
事实上。。。注意,其中一条评论中有一个更好的实现。如果我有时间,我会把它移到体内
编辑:好的,我有一个稍微缩写的版本-它只返回最大元素,使用给定的选择器。也不需要做投影——如果需要的话,以后再做一次。请注意,您可以删除TValue
上的约束,改用Comparer.Default
,或者使用一个重载,允许将比较指定为另一个参数
public static TSource MaxBy<TSource, TValue>(this IEnumerable<TSource> source,
Func<TSource, TValue> selector)
where TValue : IComparable<TValue>
{
TValue maxValue = default(TValue);
TSource maxElement = default(TSource);
bool gotAny = false;
foreach (TSource sourceValue in source)
{
TValue value = selector(sourceValue);
if (!gotAny || value.CompareTo(maxValue) > 0)
{
maxValue = value;
maxElement = sourceValue;
gotAny = true;
}
}
if (!gotAny)
{
throw new InvalidOperationException("source is empty");
}
return maxElement;
}
我就是这样让它工作的。也许有更好的方法,我不确定:
decimal de = d.Max(p => p.Name.Length);
Model a = d.First(p => p.Name.Length == de);
使用扩展方法有什么好处吗
也许一个简单迭代列表的方法或过程就足够了
大意是
Dim result as string = models(0).Name
for each m as Model in models
if m.Name.length > result.length then
result = m.Name
end if
next
Dim结果为字符串=模型(0)。名称
对于模型中的每个m as模型
如果m.Name.length>result.length,则
结果=m.名称
如果结束
下一个
另一种方法可能是:
var item = (from m in models select m orderby m.Name.Length descending).FirstOrDefault();
第一个是长度最长的。这里有另一种方法。有一个版本的Max
不采用任何标准,而是使用IComparable
。因此,我们可以提供一种将任何内容封装在可比较对象中的方法,委托提供比较
public class Comparable<T> : IComparable<Comparable<T>>
{
private readonly T _value;
private readonly Func<T, T, int> _compare;
public Comparable(T v, Func<T, T, int> compare)
{
_value = v;
_compare = compare;
}
public T Value { get { return _value; } }
public int CompareTo(Comparable<T> other)
{
return _compare(_value, other._value);
}
}
公共类可比:i可比
{
私有只读T_值;
私有只读函数比较;
公共可比(电视、职能比较)
{
_值=v;
_比较=比较;
}
公共T值{get{return_值;}}
公共国际比较(可比其他)
{
返回_比较(_值,其他._值);
}
}
那么我们可以说:
Model maxModel = models.Select(m => new Comparable<Model>(m, (a, b) => a.Name.Length - b.Name.Length)).Max().Value;
Model maxModel=models.选择(m=>newcompariable(m,(a,b)=>a.Name.Length-b.Name.Length)).Max()值;
这涉及到很多额外的分配,但在学术上有点有趣(我认为)。你可以使用聚合。不需要编写新的扩展方法就可以完成
models.Aggregate(
new KeyValuePair<Model, int>(),
(a, b) => (a.Value < b.Name.Length) ? new KeyValuePair<Model, int>(b, b.Name.Length) : a,
a => a.Key);
models.Aggregate(
新的KeyValuePair(),
(a,b)=>(a.Valuea.Key);
这样做的问题是它必须遍历列表两次。这可能非常昂贵(或者需要缓冲数据流)。在可能的情况下,一次完成整个过程会更好。如果您正在编写将在未知上下文中使用的库代码,Jon是对的。如果您了解上下文,就可以知道两次通行证是否昂贵。如果我知道上下文,我会写d.OrderBy(p=>p.Name.Length).First()
。恐怕models.Max(model=>model.Name.Length)返回int(Length)no-a模型,所以你不能像这样使用它(我指的是最后一行)@agnieszka:这是使用内置的Max方法,而不是我在回答中给出的方法。为了更清楚,您可以将新的名称重命名为“MaxElement”。编辑答案以反映这一点。如果有一个集多次使用比较值,会发生什么情况:即{5,3,5,2}。如果我的理解正确,将返回元素0。但是,是什么使元素0比具有相同值的元素2更特殊呢。。。也许返回一个包含最大值的列表会有所帮助?在我看来,这很蹩脚内置的max函数不是这样自然工作的——这不是更好/更直观吗?内置的Enumerable.max()刚刚被破坏了。如果Max()的编写方法正确,那么它现在所做的可以写成“models.Max(model=>model.Name.Length).Select(model=>model.Name);”。(理由是它同时处理单结果和多结果情况)这需要缓冲整个流,并对基本的O(n)操作进行排序-O(n log n)。换句话说:Max()只需要对第一个元素进行排序,但您的算法需要对所有元素进行排序。有人曾经告诉过我“委托与只有一个成员的接口相同“.你基本上是在Func和IComparable之间建立了一个Adapater。我喜欢。分配成本并不严重:规模不大,GC可以处理清理工作。谢谢。我已经对它进行了更新,使用了通用的IComparable,从而消除了强制转换。关于委托和一个成员接口,请看——他在一个提议的Java语言特性中讨论了这一方面。
public class Comparable<T> : IComparable<Comparable<T>>
{
private readonly T _value;
private readonly Func<T, T, int> _compare;
public Comparable(T v, Func<T, T, int> compare)
{
_value = v;
_compare = compare;
}
public T Value { get { return _value; } }
public int CompareTo(Comparable<T> other)
{
return _compare(_value, other._value);
}
}
Model maxModel = models.Select(m => new Comparable<Model>(m, (a, b) => a.Name.Length - b.Name.Length)).Max().Value;
models.Aggregate(
new KeyValuePair<Model, int>(),
(a, b) => (a.Value < b.Name.Length) ? new KeyValuePair<Model, int>(b, b.Name.Length) : a,
a => a.Key);