0

I am puzzled by the expected behaviour or the family of LINQ methods SingleOrDefault, FirstOrDefault, LastOrDefault and ElementAtOrDefault methods, operating on a database.

Should they return:

  1. null, or
  2. a new object populated with default values?
Andy Dent
  • 17,578
  • 6
  • 88
  • 115

3 Answers3

1

They return the item in the sequence or the default value of the item if the sequence is empty (or in the case of 'ElementAtOrDefault' if the index specified is out of range).

The below excerpt is from the documentation for default:

The solution is to use the default keyword, which will return null for reference types and zero for numeric value types. For structs, it will return each member of the struct initialized to zero or null depending on whether they are value or reference types.

Link to default documentation: https://msdn.microsoft.com/en-us/library/xwth0h0d.aspx

astidham2003
  • 966
  • 1
  • 11
  • 33
0

From the Return Value section of Enumerable.FirstOrDefault

default(TSource) if source is empty; otherwise, the first element in source.


default(T) for Reference Types is null in almost all cases, but for Value Types is not null. Examples:

int i; // 0
var j = default(int);    // 0
var b = default(bool);   // false
var s = default(string); // null
var s = default(object); // null

var i0 = (new    int[] { }).FirstOrDefault(); // 0 
var s0 = (new string[] { }).FirstOrDefault(); // null

There is also DefaultIfEmpty:

var i1 = new int[] { }.DefaultIfEmpty(-1).ToArray(); // { -1 }
Slai
  • 22,144
  • 5
  • 45
  • 53
0

Here are some definition's using reflector.

FirstOrDefault

public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    IList<TSource> list = source as IList<TSource>;
    if (list != null)
    {
        if (list.Count > 0)
        {
            return list[0];
        }
    }
    else
    {
        using (IEnumerator<TSource> enumerator = source.GetEnumerator())
        {
            if (enumerator.MoveNext())
            {
                return enumerator.Current;
            }
        }
    }
    return default(TSource);
}

SingleOrDefault

    public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source)
    {
        if (source == null)
        {
            throw Error.ArgumentNull("source");
        }
        IList<TSource> list = source as IList<TSource>;
        if (list != null)
        {
            switch (list.Count)
            {
                case 0:
                    return default(TSource);

                case 1:
                    return list[0];
            }
        }
        else
        {
            using (IEnumerator<TSource> enumerator = source.GetEnumerator())
            {
                if (!enumerator.MoveNext())
                {
                    return default(TSource);
                }
                TSource current = enumerator.Current;
                if (!enumerator.MoveNext())
                {
                    return current;
                }
            }
        }
        throw Error.MoreThanOneElement();
    }

LastOrDefault

    public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source)
    {
        if (source == null)
        {
            throw Error.ArgumentNull("source");
        }
        IList<TSource> list = source as IList<TSource>;
        if (list != null)
        {
            int count = list.Count;
            if (count > 0)
            {
                return list[count - 1];
            }
        }
        else
        {
            using (IEnumerator<TSource> enumerator = source.GetEnumerator())
            {
                if (enumerator.MoveNext())
                {
                    TSource current;
                    do
                    {
                        current = enumerator.Current;
                    }
                    while (enumerator.MoveNext());
                    return current;
                }
            }
        }
        return default(TSource);
    }

ElementAtOrDefault

public static TSource ElementAtOrDefault<TSource>(this IEnumerable<TSource> source, int index)
    {
        if (source == null)
        {
            throw Error.ArgumentNull("source");
        }
        if (index >= 0)
        {
            IList<TSource> list = source as IList<TSource>;
            if (list != null)
            {
                if (index < list.Count)
                {
                    return list[index];
                }
            }
            else
            {
                using (IEnumerator<TSource> enumerator = source.GetEnumerator())
                {
                    while (enumerator.MoveNext())
                    {
                        if (index == 0)
                        {
                            return enumerator.Current;
                        }
                        index--;
                    }
                }
            }
        }
        return default(TSource);
    }
Rohit
  • 10,056
  • 7
  • 50
  • 82
  • The source code is also available here for Enumerable: http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs , and here for IQueryable: http://referencesource.microsoft.com/#System.Core/System/Linq/IQueryable.cs – astidham2003 Sep 02 '16 at 19:52