扩展LINQ接受可枚举枚举
在使用Linq扩展时,看到这样的代码是正常的:
IEnumerable<int> enumerable = GetEnumerable(); int sum = 0; if (enumerable != null) { sum = enumerable.Sum(); }
为了提高代码质量,我编写了下面的扩展方法来检查可空的枚举types并打断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();
我现在的问题是:
- 我的扩展方法在运行时会受到哪些处罚?
- 以这种方式扩展linq是一个好习惯吗?
更新:我的目标框架是:3.5
- 你将会有一个方法调用的开销,除非你在一个严格的循环或者一个性能严格的场景下运行它,否则它将被忽略掉。 与数据库调用或写入文件系统相比,这只是一个阴影。 请注意,该方法可能不会被内联,因为它是一个枚举器。
- 这都是关于可读性/可维护性的。 当我看到
GetEnumerable().IgnoreIfEmpty().Sum();
时,我希望发生什么GetEnumerable().IgnoreIfEmpty().Sum();
? 在这种情况下,这是有道理的。
请注意,使用C#6我们可以使用以下语法: GetEnumerable()?.Sum()
返回一个int?
。 你可以写GetEnumerable()?.Sum() ?? 0
GetEnumerable()?.Sum() ?? 0
或GetEnumerable()?.Sum().GetValueOrDefault()
获得一个默认为零的非空整数。
如果你真的关心性能,你也可以稍微重构你的方法,使它不是一个枚举器。 这可能会增加内联的机会,尽pipe我不知道JIT编译器的“神秘”逻辑:
public static IEnumerable<T> IgnoreIfEmpty<T>(this IEnumerable<T> enumerable) { if (enumerable == null) return Enumerable.Empty<T>(); return enumerable; }
更一般的关于扩展Linq,我认为只要代码有意义就没有问题。 MSDN甚至有一篇关于它的文章 。 如果你看一下标准的Where
,在Linq中Select
方法,忘记它们在那里的性能优化,那么这些方法都是单线程方法。
我的扩展方法在运行时会受到哪些处罚?
你的扩展方法被转换成一个状态机,所以这是最小的开销,但这不应该引人注目。
以这种方式扩展linq是一个好习惯吗?
在你的问题中你说:
在使用Linq扩展时,看到这样的代码是正常的( 在这里插入枚举null检查 )
我不同意。 通常的做法是不要在IEnumerable<T>
地方返回null 。 大多数情况下应该返回一个空集合(或IEnumerable
),将null
留给例外 ,因为null不是空的 。 这将使你的方法完全多余。 在需要的地方使用Enumerable.Empty<T>
。
你可以跳过额外的扩展方法,并使用null合并运算符 – 这是它的目的,一次检查可空性应该比另一个状态机高效得多:
IEnumerable<int> enumerable = GetEnumerable(); int sum = 0; sum = (enumerable ?? Enumerable.Empty<int>()).Sum();
大多数情况下,我们编写了大量的代码,仅仅是因为我们被创造的美好所吸引,而不是因为我们真的需要它 ,然后我们把它称为抽象,可重用性,可扩展性等等。
这块原材料的可读性较差,可扩展性较差,重复性较差或较慢:
var sum = GetEnumerable().Where(a => a != null).Sum();
你写的代码越less – 你testing的代码越less – 保持简单。 顺便说一句 – 如果你能certificate这一点,写扩展方法是很好的。