FirstOrDefault 不会在整数列表上返回 null

时间:2021-07-24 10:17:29

标签: c# linq

当我在整数或布尔值的空列表上使用 FirstOrDefault() 时,我得到 0false 而不是 null

我知道这是设计使然,因为它返回 default(T),但我如何强制它返回 null

var emptyList = new List<int>();

emptyList.FirstOrDefault() == 0; // I want to receive null instead

3 个答案:

答案 0 :(得分:2)

这就是它被称为 FirstOrDefault 而不是 FirstOrNull 的原因。后者意味着在值类型(TNullable<T>)的情况下,返回类型将与元素类型不同。

(我看到你已经回答了你自己的问题,所以我只是用 FirstOrNull 扩展方法作为通用解决方案来完成它):

// the generalized version of Bouke's answer for the problem:
public static T? FirstOrNull<T>(this IEnumerable<T> source)
    where T : struct
    => source.Cast<T?>().FirstOrDefault();

当然,如果性能很重要,您可以进一步改进它:

public static T? FirstOrNull<T>(this IEnumerable<T> source)
    where T : struct
{
    // Shortcuts for special cases. FirstOrDefault also has something like this
    // (but it's not quite useful on the enumerator returned by Cast<T>)
    if (source is ICollection<T> collection)
    {
        if (collection.Count == 0)
            return null;
        if (collection is IList<T> list)
            return list[0];
    }

    // general solution
    using var enumerator = source?.GetEnumerator() ?? throw new ArgumentNullException(nameof(source));
    return enumerator.MoveNext() ? enumerator.Current : default(T?);
}

答案 1 :(得分:1)

要使 FirstOrDefault()LastOrDefault()SingleOrDefault() 返回 null 作为 values 列表中的默认值(而不是 < em>引用):

使用 IEnumerable<T>.Cast<T?>() 将您的元素转换为 nullable

var emptyList = new List<int>();

emptyList.FirstOrDefault() == 0;
emptyList.Cast<int?>().FirstOrDefault() == null;
  • IEnumerable<T>.FirstOrDefault() 将在没有结果时返回 default(T)
    • 当T是值类型/结构时,比如int,它会返回default(T),例如对于 int 将是 0
    • 当 T 是引用类型可空值类型时,例如int?bool?或一个 class,然后它会返回 null,作为 default(int?) == null

答案 2 :(得分:0)

如果您不想使用 Nullable 列表,您可以滚动您自己的返回 null 的扩展方法,如果结构/原语列表不包含任何项目,否则为第一个匹配项。

            public static T? FirstOrNull<T>(this IEnumerable<T> collection, Predicate<T> validator = null)
                    where T : struct {
                
                foreach (var item in collection) {
                    if (validator?.Invoke(item) != false) {
                        return item;
                    }
                }
                return default;
            }

//invocations

            int[] list = new int[0];
            int? match = list.FirstOrNull(); // => null
            list  = new [] { 1, 2, 3, 4, 5, 6 };
            match = list.FirstOrNull(i => i == 42); // => null
            match = list.FirstOrNull(); // => 1
            match = list.FirstOrNull(i => i % 3 == 0); // => 3