A different take on FirstOrDefault

╄→гoц情女王★ 提交于 2019-12-05 17:50:46

问题


The IEnumerable extension method FirstOrDefault didn't exactly do as I wanted so I created FirstOrValue. Is this a good way to go about this or is there a better way?

public static T FirstOrValue<T>(this IEnumerable<T> source, Func<T, bool> predicate, T value)
{
    T first = source.FirstOrDefault(predicate);
    return Equals(first, default(T)) ? value : first;
}

回答1:


Your code is probably incorrect; you probably haven't considered all of the cases.

Of course, we cannot know if any code is correct or incorrect until we have a spec. So start by writing a one-line spec:

"FirstOrValue<T> takes a sequence of T, a predicate, and a value of T, and returns either the first item in the sequence that matches the predicate if there is one, or, if there is not, the stated value."

Does your attempt actually implement that spec? Certainly not! Test it:

int x = FirstOrValue<int>( new[] { -2, 0, 1 }, y=>y*y==y, -1);

this returns -1. The correct answer according to the spec is 0. The first item that matches the predicate is zero, so it should be returned.

A correct implementation of the spec would look like:

public static T FirstOrValue<T>(this IEnumerable<T> sequence, Func<T, bool> predicate, T value)
{
    if (sequence == null) throw new ArgumentNullException("sequence");
    if (predicate == null) throw new ArgumentNullException("predicate");
    foreach(T item in sequence)
        if (predicate(item)) return item;
    return value;
}

Always write a spec first, even if it's only a single sentence.




回答2:


default(T) will return null by default for reference types.

I would do this

public static T FirstOrValue<T>(this IEnumerable<T> source, Func<T, bool> predicate, T value)
{
    T first = source.FirstOrDefault(predicate);
    return first ?? value;
}



回答3:


Since this is an overload, it's worth mentioning the version with no predicate.

public static T FirstOrValue<T>(this IEnumerable<T> sequence, T value)
{
    if (sequence == null) throw new ArgumentNullException("sequence");
    foreach(T item in sequence) 
        return item; 
    return value;
}



回答4:


Seems reasonable to me if you want to tweak the readability instead of using DefaultIfEmpty.

You could also create an override that uses a lambda if the creation of the default value is expensive, creating it only if necessary.

public static T FirstOrValue<T>(this IEnumerable<T> source, Func<T, bool> predicate, Func<T> getValue)
{
    T first = source.FirstOrDefault(predicate);
    return Equals(first, default(T)) ? getValue() : first;
}


来源:https://stackoverflow.com/questions/1708846/a-different-take-on-firstordefault

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!