结构集合的 FirstOrDefault() 结果?
Posted
技术标签:
【中文标题】结构集合的 FirstOrDefault() 结果?【英文标题】:FirstOrDefault() result of a struct collection? 【发布时间】:2013-03-14 10:46:39 【问题描述】:所以我收集了struct
s 的集合(它实际上是一个 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
调用的结果是什么?会不会抛出异常?
顺便说一句,这不应该永远不会发生,但总比抱歉更安全。
【问题讨论】:
【参考方案1】:如果您的集合为空,FirstOrDefault
将返回 default(OptionalExtras)
。结构的默认值是其所有值依次默认初始化的结构(即零、空等)。
如果您假设会有一个元素并且您的代码不适用于空集合,请改用First()
,因为当您的集合为空时会引发异常。快速失败通常比返回错误数据要好。
如果你不能假设会有一个元素,又不能处理结构体默认初始化,你可以将集合中的结构体设为nullable value type,例如如下:
OptionalExtras
.Where(w => w.Code == optExtra.Code)
.Cast<OptionalExtra?>()
.FirstOrDefault();
这样,即使是结构,您也可以获得空返回。这里的关键思想是扩展可能值集以包含OptionalExtra
以外的其他内容,以允许检测空列表。如果您不喜欢可空值,您可以改用 Maybe<>
实现(不是 .NET 内置),或使用空列表或单例列表(例如 .Take(1).ToArray()
。但是,可空结构可能是您的最佳选择.
TL;DR;
如果序列为空,.FirstOrDefault<T>()
返回default(T)
如果您认为列表不为空,请改用.First()
。
当您不能假定列表非空时,转换为可为空,然后使用 .FirstOrDefault<T>()
。
【讨论】:
【参考方案2】:正如其他人所说,当没有元素匹配时,您的代码结果将是:
default( OptionalExtra )
如果您希望返回 null,您可以将列表转换为 OptionalExtra?
OptionalExtra? multiOptExtra = OptionalExtras.Cast<OptionalExtra?>().Where( ...
然后您可以测试null
【讨论】:
我无法控制该结构,它是来自第三方的 WCF DataContract。还是谢谢 您不需要控制结构 - 您只是暂时将List<OptionalExtra>
的元素转换为有效的 LINQ 的 OptionalExtra?
我想我更喜欢@EamonNerbonne 的回答。如果它不存在,我宁愿它失败,而不是花费权力铸造。在这种情况下,失败应该是非常罕见的,因此最好针对最可能的情况进行优化。就像我说的,无论如何,ta
+1 当您不知道列表是否为非空时,这是要走的路。【参考方案3】:
如果default(OptionExtra)
仍然是有效值,最好将您的代码更改为此
var results = OptionalExtras.Where(w => w.Code == optExtra.Code).Take(1).ToList();
if (results.Any())
multiOptExtra = results[0]
【讨论】:
首选ToArray
而不是ToList
- 数组更快,使用更少的内存,更严格(即很少有意外错误),并且语法更短。如果您使用 LINQ,几乎没有理由使用 List<>
。
@EamonNerbonne 除非您想将项目添加到结果中。
@EamonNerbonne 这似乎是夸大其词(请参阅***.com/a/1106012/83171)和微优化的味道。 ToArray
和 ToList
方法之间的性能差异是微不足道的,List<T>
提供了更多功能,使用语法相似并且实际上比数组更安全,因为它们的协方差被破坏,允许像 var x = new string[1]; ((object[])x)[0] = 1;
这样的代码。在这种情况下,当您有许多单元素数组时,这是有道理的,但我不会说“几乎从不将 List.ToList
和.ToArray
;但在每次后续使用该集合时(以及 GC 对象计数加倍)。其次,如果你正在铸造,你总是会导致异常(但你所说的可能会导致问题,不可否认)。然而,在实践中,List.Add
的存在更可能导致问题,List.Reverse
的存在隐含地替换了 LINQ 的Reverse
。
我明白你的意思,并且我同意数组的性能更高 - 它们被用作许多其他集合的基础集合。当然List<>
有它的问题。我仍然不同意您的说法,即几乎没有理由将它们与 LINQ 一起使用。作为旁注,我对.ToArray()
和.ToList()
方法进行了微基准测试,在我的笔记本电脑上,.ToArray()
方法似乎触发了更多的垃圾回收。你可以自己试试 - gist.github.com/vas6ili/5164182.【参考方案4】:
结果将是你的结构的默认值,例如default(OptionalExtras)
.
而对于引用类型,默认值为null
。
【讨论】:
【参考方案5】:它为您的结构提供默认值,如下所示
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);
【讨论】:
【参考方案6】:如果要检查 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 来访问元素。
【讨论】:
但结构永远不能为空? 那个?表示法将结构转换为 Nullable 对象,因此是 myStructVar.Value 要求。不确定这样做是否是一个好的设计,但它很简单。 但这并不能回答问题。我问一个结构(值类型)的 FirstOrDefault 的结果是什么。你说如果我使我的结构可以为空(将其更改为引用类型),我将得到一个空值。这不是我要求的。 这不是对您问题的直接回答,但由于您已批准(且正确)答案,我添加了一个案例,您可以安全检查您的 LINQ 查询是否为空。【参考方案7】:假设 Code 是我回答的目的的字符串,您应该能够测试该值的默认值。
OptionalExtra multiOptExtra = OptionalExtras.Where(w => w.Code == optExtra.Code).FirstOrDefault();
if (multiOptExtra.Code != null)
【讨论】:
如果Where
返回null
multiOptExtra.Code != null
会抛出一个nullReferenceExcepetion
,这实际上也不能回答这个问题,这就是firstordefault()
返回一个struct
收藏。
@Liam:不会返回 Null。我只是采用了您的代码的变体,运行它,FirstOrDefault 返回了一个不为空的空结构。见:harriergroup.com/mll/imagesonweb/struct.png
嗯,实际上你的权利......我忘了这是一个结构。仍然没有回答我的问题。以上是关于结构集合的 FirstOrDefault() 结果?的主要内容,如果未能解决你的问题,请参考以下文章
何时使用 .First 以及何时将 .FirstOrDefault 与 LINQ 一起使用?