在使用Linq扩展时,看到这样的代码是正常的:
IEnumerable<int> enumerable = GetEnumerable();
int sum = 0;
if (enumerable != null)
{
sum = enumerable.Sum();
}
为了提高代码质量,我编写了以下扩展方法来检查可以为空的枚举并打破linq执行。
public static IEnumerable<T> IgnoreIfEmpty<T>(this IEnumerable<T> enumerable)
{
if (enumerable == null) yield break;
foreach (var item in enumerable)
{
yield return item;
}
}
所以,我可以重构代码:
var sum = GetEnumerable().IgnoreIfEmpty().Sum();
我现在的问题:
更新 我的目标框架是:3.5
答案 0 :(得分:40)
在运行时我的扩展方法有哪些处罚?
你的扩展方法被转换为状态机,所以这是最小的开销,但这不应该是显而易见的。
以这种方式扩展linq是一个好习惯吗?
在你的问题中,你说:
在使用Linq扩展时,看到这样的代码是正常的( insert enumerable null check here here )
我不同意。 常规练习表示don't return null where an IEnumerable<T>
is expected。大多数情况下应返回空集合(或IEnumerable
),将null
留给例外,因为null is not empty。这会使您的方法完全冗余。在需要的地方使用Enumerable.Empty<T>
。
答案 1 :(得分:29)
GetEnumerable().IgnoreIfEmpty().Sum();
时,我会发生什么?在这种情况下,它是有道理的。请注意,使用C#6,我们可以使用以下语法:GetEnumerable()?.Sum()
,它返回int?
。您可以编写GetEnumerable()?.Sum() ?? 0
或GetEnumerable()?.Sum().GetValueOrDefault()
来获取一个默认为零的非空整数。
如果你真的关心性能,你也可以稍微重构你的方法,这样它就不是一个枚举器。这可能会增加内联的机会,虽然我不知道“奥术”。 JIT编译器的逻辑:
public static IEnumerable<T> IgnoreIfEmpty<T>(this IEnumerable<T> enumerable)
{
if (enumerable == null) return Enumerable.Empty<T>();
return enumerable;
}
更广泛地说,扩展Linq,我认为只要代码有意义就完全没问题。 MSDN甚至有关于它的an article。如果您查看Linq中的标准Where
, Select
方法,并忘记它们在那里的性能优化,那么这些方法大多都是单行方法。
答案 2 :(得分:14)
您可以跳过其他扩展方法并使用null coalescing operator - 这就是它的用途,并且一次性检查可空性应该比另一个状态机更有效:
IEnumerable<int> enumerable = GetEnumerable();
int sum = 0;
sum = (enumerable ?? Enumerable.Empty<int>()).Sum();
答案 3 :(得分:-1)
大多数时候,我们编写了大量代码只是因为我们被创造的美丽所迷惑 - 不是因为我们真的需要它 - 然后我们称之为抽象,可重用性,可扩展性,等等。
这个原始部分是否具有较低的可读性或较低的可扩展性或较少的可重用性或较慢的速度:
var sum = GetEnumerable().Where(a => a != null).Sum();
您编写的代码越少 - 您测试的代码越少 - 保持简单。 顺便说一句 - 如果可以证明它是合理的,那么编写扩展方法是很好的。