扩展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(); 

我现在的问题是:

  1. 我的扩展方法在运行时会受到哪些处罚?
  2. 以这种方式扩展linq是一个好习惯吗?

更新:我的目标框架是:3.5

  1. 你将会有一个方法调用的开销,除非你在一个严格的循环或者一个性能严格的场景下运行它,否则它将被忽略掉。 与数据库调用或写入文件系统相比,这只是一个阴影。 请注意,该方法可能不会被内联,因为它是一个枚举器。
  2. 这都是关于可读性/可维护性的。 当我看到GetEnumerable().IgnoreIfEmpty().Sum();时,我希望发生什么GetEnumerable().IgnoreIfEmpty().Sum(); ? 在这种情况下,这是有道理的。

请注意,使用C#6我们可以使用以下语法: GetEnumerable()?.Sum()返回一个int? 。 你可以写GetEnumerable()?.Sum() ?? 0 GetEnumerable()?.Sum() ?? 0GetEnumerable()?.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这一点,写扩展方法是很好的。