A different take on FirstOrDefault

坚强是说给别人听的谎言 提交于 2019-12-04 03:19:09

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.

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;
}

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;
}

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