A different take on FirstOrDefault

前端 未结 4 1170
抹茶落季
抹茶落季 2021-02-20 14:39

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?



        
相关标签:
4条回答
  • 2021-02-20 15:04

    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;
    }
    
    0 讨论(0)
  • 2021-02-20 15:11

    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;
    }
    
    0 讨论(0)
  • 2021-02-20 15:14

    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;
    }
    
    0 讨论(0)
  • 2021-02-20 15:29

    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.

    0 讨论(0)
提交回复
热议问题