结构体集合的FirstOrDefault()结果?

问题描述 投票:0回答:8

所以我得到了

struct
的集合(它实际上是一个 WCF 数据契约,但我认为这与这里无关)。

List<OptionalExtra> OptionalExtras;

OptionalExtra
struct

public partial struct OptionalExtra

现在我正在运行以下语句:

OptionalExtra multiOptExtra = OptionalExtras.Where(w => w.Code == optExtra.Code).FirstOrDefault();
if (multiOptExtra != null)
{

}

现在无法编译:

运算符!=不能应用于OptionalExtra类型的操作数 和

'<null>'

经过一番谷歌搜索后,我意识到这是因为

OptionalExtra
struct
。我认为除非定义为可为空类型,否则它不可为空?

所以我的问题是,如果我的

where
语句没有返回结果,那么
FirstOrDefault
调用的结果是什么?会抛出异常吗?

顺便说一句,这应该永远不会发生,但安全总比后悔好。

linq c#-4.0 struct
8个回答
83
投票

如果您的收藏为空,

FirstOrDefault
将返回
default(OptionalExtras)
。结构体的默认值是该结构体的所有值依次默认初始化(即零、空等)。

如果您假设存在一个元素,并且您的代码不适用于空集合,则使用

First()
代替,因为当您的集合为空时,这会引发异常。一般来说,快速失败比返回错误数据要好。

如果你不能假设会有一个元素,但也不能处理结构默认初始化,你可以将集合中的结构设为可空值类型,例如如下:

OptionalExtras
    .Where(w => w.Code == optExtra.Code)
    .Cast<OptionalExtra?>()
    .FirstOrDefault();

通过这种方式,即使对于结构体也可以获得空返回。这里的关键思想是扩展可能值的集合以包含除

OptionalExtra
之外的其他内容,以允许检测空列表。如果您不喜欢可为 null,则可以使用
Maybe<>
实现(不是 .NET 内置),或使用空列表或单个列表(例如
.Take(1).ToArray()
)。但是,可为 null 的结构可能是您的最佳选择.

TL;博士;

    如果序列为空,则
  • .FirstOrDefault<T>()
    返回
    default(T)
  • 如果您假设列表非空,请改用
    .First()
  • 转换为可为空,然后在无法假设列表非空时使用
    .FirstOrDefault<T>()

10
投票

正如其他人所说,当没有元素匹配时,代码的结果将是:

default( OptionalExtra )

如果您想要返回 null,您可以将列表转换为

OptionalExtra?

OptionalExtra? multiOptExtra = OptionalExtras.Cast<OptionalExtra?>().Where( ...

然后您可以测试

null


4
投票

如果

default(OptionExtra)
仍然是有效值,最好将代码更改为这样

var results = OptionalExtras.Where(w => w.Code == optExtra.Code).Take(1).ToList();
if (results.Any()) {
   multiOptExtra = results[0]
}

3
投票

结果将是结构的默认值,例如

default(OptionalExtras)

而对于引用类型,默认值为

null


2
投票

它为您的结构提供默认值,如下所示

int[] numbers = { };
int first = numbers.FirstOrDefault();
Console.WriteLine(first);//this print 0 as output 

要处理的其他选项是使用默认值,如下所示

List<int> months = new List<int> { };
// Setting the default value to 1 by using DefaultIfEmpty() in the query. 
int firstMonth2 = months.DefaultIfEmpty(1).First();
Console.WriteLine("The value of the firstMonth2 variable is {0}", firstMonth2);

0
投票

如果要检查 null,请使用 System.Nullable 集合:

var OptionalExtras = new List<OptionalExtra?>();
/* Add some values */
var extras = OptionalExtras.FirstOrDefault(oe => oe.Value.Code == "code");
if (extras != null)
{
    Console.WriteLine(extras.Value.Code);
}

请注意,您必须使用 Value 来访问该元素。


0
投票

所有答案都要求将 IEnumerable 元素单独转换为 Nullable 类型,然后在可为 Nullable 类型上运行谓词,如下所示:

var wantedValue = myEnumerableStruct.Cast<MyStruct?>()
                            .FirstOrDefault(s => s.HasValue && s.Value == whatever)

我认为使用类似的扩展更容易

internal static class LinqExtensions
{
    public static T? FirstOrNull<T>(this IEnumerable<T> values, Func<T, bool> predicate) where T : struct
    {
        foreach (var v in values)
        {
            if (predicate(v))
            {
                return v;
            }
        }
        return null;
    }
}

然后像这样消费:

myResult = myEnumerableStruct.FirstOrNull(s => s == whatever)

-1
投票

假设 Code 是一个用于我的答案的字符串,您应该能够测试该值的默认值。

OptionalExtra multiOptExtra = OptionalExtras.Where(w => w.Code == optExtra.Code).FirstOrDefault();
if (multiOptExtra.Code != null)
{

}
© www.soinside.com 2019 - 2024. All rights reserved.