所以我得到了
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
调用的结果是什么?会抛出异常吗?
顺便说一句,这应该永远不会发生,但安全总比后悔好。
如果您的收藏为空,
FirstOrDefault
将返回default(OptionalExtras)
。结构体的默认值是该结构体的所有值依次默认初始化(即零、空等)。
如果您假设存在一个元素,并且您的代码不适用于空集合,则使用
First()
代替,因为当您的集合为空时,这会引发异常。一般来说,快速失败比返回错误数据要好。
如果你不能假设会有一个元素,但也不能处理结构默认初始化,你可以将集合中的结构设为可空值类型,例如如下:
OptionalExtras
.Where(w => w.Code == optExtra.Code)
.Cast<OptionalExtra?>()
.FirstOrDefault();
通过这种方式,即使对于结构体也可以获得空返回。这里的关键思想是扩展可能值的集合以包含除
OptionalExtra
之外的其他内容,以允许检测空列表。如果您不喜欢可为 null,则可以使用 Maybe<>
实现(不是 .NET 内置),或使用空列表或单个列表(例如 .Take(1).ToArray()
)。但是,可为 null 的结构可能是您的最佳选择.
.FirstOrDefault<T>()
返回default(T)
.First()
。.FirstOrDefault<T>()
。正如其他人所说,当没有元素匹配时,代码的结果将是:
default( OptionalExtra )
如果您想要返回 null,您可以将列表转换为
OptionalExtra?
OptionalExtra? multiOptExtra = OptionalExtras.Cast<OptionalExtra?>().Where( ...
然后您可以测试
null
如果
default(OptionExtra)
仍然是有效值,最好将代码更改为这样
var results = OptionalExtras.Where(w => w.Code == optExtra.Code).Take(1).ToList();
if (results.Any()) {
multiOptExtra = results[0]
}
结果将是结构的默认值,例如
default(OptionalExtras)
。
而对于引用类型,默认值为
null
。
它为您的结构提供默认值,如下所示
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);
如果要检查 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 来访问该元素。
所有答案都要求将 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)
假设 Code 是一个用于我的答案的字符串,您应该能够测试该值的默认值。
OptionalExtra multiOptExtra = OptionalExtras.Where(w => w.Code == optExtra.Code).FirstOrDefault();
if (multiOptExtra.Code != null)
{
}