可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
i am new to .net 3.5.
I have a collection of items:
IList<Model> models;
where
class Model
{
public string Name
{
get;
private set;
}
}
I would like to get the element, which has the longest name's length.
I tried
string maxItem = models.Max<Model>(model => model.Name.Length);
but it of course returns the maximum length (and I need a Model
object). I know there is a way of doing this using the extension methods but I don't know how.
回答1:
This is how I got it to work. Maybe there's a better way, I'm not sure:
decimal de = d.Max(p => p.Name.Length);
Model a = d.First(p => p.Name.Length == de);
回答2:
There isn't a built-in way of doing this, unfortunately - but it's really easy to write an extension method to do it.
It was in one of my very first blog posts, in fact... note that there's a better implementation in one of the comments. I'll move it into the body if I get time.
EDIT: Okay, I have a slightly abbreviated version - it just returns the maximal element, using the given selector. No need to do a projection as well - do that once afterwards if you need to. Note that you could remove the constraint on TValue
and use Comparer<TValue>.Default
instead, or have an overload which allows the comparison to be specified as another parameter.
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;
}
Sample use: (note type inference):
string maxName = models.MaxBy(model => model.Name.Length).Name;
回答3:
Here's another way of doing it. There's a version of Max
that takes no criterion, and uses IComparable
. So we could provide a way to wrap anything in a comparable object, with a delegate providing the comparison.
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);
}
}
Then we can say:
Model maxModel = models.Select(m => new Comparable<Model>(m, (a, b) => a.Name.Length - b.Name.Length)).Max().Value;
This involves a lot of extra allocation, but it's sort of academically interesting (I think).
回答4:
You can use Aggregate. It can be done without writing new extension method.
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);
回答5:
Is there anything gained by using the extension methods?
Perhaps a method or procedure with a simple iteration of the list would suffice?
Something to the effect of
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
回答6:
Another way might be:
var item = (from m in models select m orderby m.Name.Length descending).FirstOrDefault();
First one will be the one with the longest length.