我有一个包含1,000,000个复杂对象的List。我需要创建另一个包含这些对象子集的列表,保持原始列表不变。在代码的这一点上,我确定bigList不是null,并且它至少有一个项目。
我的原始代码:
var smallList = bigList.Where(csvrec => csvrec.PreApprovalAmount <= 0 || csvrec.PreApprovalAmount > csvrec.ReplacementAmount).ToList();
我的团队负责人说我的代码存在一些问题。他说。可能会导致null,并且调用.ToList()会导致null异常。因此,为了避免这种情况,他说我需要将我的代码更改为:
var smallList = new List<CSVLines>();
if(bigList.Any(csvrec => csvrec.PreApprovalAmount <= 0 || csvrec.PreApprovalAmount > csvrec.ReplacementAmount))
{
smallList = bigList.Where(csvrec => csvrec.PreApprovalAmount <= 0 || csvrec.PreApprovalAmount > csvrec.ReplacementAmount).ToList();
}
我说错了吗?我团队提出的修改建议是否基本上使创建此列表的工作量增加一倍而没有真正的好处?
答案 0 :(得分:5)
带谓词的.Any必须先生成结果列表 它寻找.Any
不,Enumerable.Any
不需要这样做。 MSDN:
只要结果可以,就会停止枚举源 确定。
该方法接受序列和谓词,然后枚举序列,直到谓词匹配一次并返回true
。如果没有项目匹配false
则返回。因此,如果第一个项目已匹配,则不需要枚举结果。在Source - 代码:
foreach (TSource element in source) {
if (predicate(element)) return true;
}
return false;
我团队的建议变更是否基本上加倍了 创建此列表的工作量没有真正的好处吗?
是的,首先检查是否有任何项匹配,然后使用Where
进行过滤是不必要的开销。它不会使开销增加一倍,因为Any
在第一次匹配时停止,但它是开销(如果没有匹配的项目则会加倍,因为序列必须枚举两次)。
.Where
可能导致null,并且调用.ToList()会导致null
例外。
不,那是不可能的。 Enumerable.Where
永远不会返回null
,它是输入序列的过滤器,如果没有项匹配则返回谓词Enumerable.Empty<T>
。
也许他感到困惑,因为查询是在ToList
处执行的,所以如果查询中某处有NullReferenceException
,那么您会在ToList
处看到此异常(或任何执行它的其他方法)。查看以下抛出异常的查询:
var query = "foo".Select(c => { throw new NullReferenceException(); return 1; });
List<int> list = query.ToList(); // exception here not in first line
答案 1 :(得分:3)
他说{{1}}可能导致
.Where
,而调用null
会导致空例外。
.ToList()
可能导致空引用异常的唯一原因是它的目标是Where
还是其谓词遇到空引用异常。如果没有匹配,null
会返回空的.Where
,而不是IEnumerable<T>
。
添加对null
的通话完全是多余的。虽然它不会重新创建整个列表,但在返回Any
或true
之前,它仍会在评估谓词时使用一些CPU周期。但它仍然是O(n),因此当找不到匹配时,将评估整个序列的条件。
添加对false
的调用也会降低代码的可读性,这可能比浪费CPU周期更糟糕。
答案 2 :(得分:0)
.Where()
只会在bigList
为空时才会导致异常。
但是,执行.Any()
不必生成列表。只要找到与谓词匹配的 first 项,它就会经过一个集合并停止。
您可能应该做的一项更改是删除ToList()
电话。尝试尽可能地坚持IEnumerable
并避免创建List对象,尤其是在处理较大的集合时。这是为了改善内存使用并避免导致OutOfMemoryExceptions而不是NullReferenceExceptions。