简体   繁体   English

扩展LINQ以接受可以为空的枚举

[英]Extending LINQ to accept nullable enumerables

While working with Linq extensions it's normal to see code like this: 在使用Linq扩展时,看到这样的代码是正常的:

IEnumerable<int> enumerable = GetEnumerable();
int sum = 0;
if (enumerable != null)
{
    sum = enumerable.Sum();
}

In order to enhance the code quality, I wrote the following extension method that checks for nullable enumerables and breaks the linq execution. 为了提高代码质量,我编写了以下扩展方法来检查可空的枚举并打破linq执行。

public static IEnumerable<T> IgnoreIfEmpty<T>(this IEnumerable<T> enumerable)
{
    if (enumerable == null) yield break;
    foreach (var item in enumerable)
    {
        yield return item;
    }
}

So, I can refactor the code to be like this: 所以,我可以重构代码,如下所示:

var sum = GetEnumerable().IgnoreIfEmpty().Sum();

My questions now: 我现在的问题:

  1. What penalties are associated with my extension method at runtime? 在运行时我的扩展方法有哪些惩罚?
  2. Is it's a good practice to extend linq that way? 以这种方式扩展linq是一个好习惯吗?

Update: My target framework is: 3.5 更新:我的目标框架是:3.5

What penalties are associated with my extension method at runtime? 在运行时我的扩展方法有哪些惩罚?

Your extension method is transformed into a state-machine, so there's the minimal overhead of that, but that shouldn't be noticeable. 你的扩展方法被转换为状态机,所以它的开销很小,但这不应该是明显的。

Is it's a good practice to extend linq that way? 以这种方式扩展linq是一个好习惯吗?

In your question you state: 在您的问题中,您声明:

While working with Linq extensions it's normal to see code like this ( insert enumerable null check here ) 在使用Linq扩展时,看到这样的代码是正常的( 在此处插入可枚举的null检查

And I beg to differ. 我不同意。 The common practice says don't return null where an IEnumerable<T> is expected . 通常的做法不要在期望IEnumerable<T>地方返回null Most cases should return an empty collection (or IEnumerable ), leaving null to the exceptional , because null is not empty . 大多数情况下应该返回一个空集合(或IEnumerable ),将null留给例外 ,因为null不为空 This would make your method entirely redundant. 这会使您的方法完全冗余。 Use Enumerable.Empty<T> where needed. 在需要的地方使用Enumerable.Empty<T>

  1. You're going to have a method call overhead, it will be negligible unless you are running it in a tight loop or a performance criticial scenario. 您将有一个方法调用开销,除非您在紧密循环或性能标准场景中运行它,否则它将是微不足道的。 It's but a shadow in comparison to something like a database call or writing to a file system. 与数据库调用或写入文件系统相比,它只是一个阴影。 Note that the method is probably not going to be inlined, since it's an enumerator. 请注意,该方法可能不会被内联,因为它是一个枚举器。
  2. It's all about readability / maintainability. 这完全取决于可读性/可维护性。 What do I expect to happen when I see GetEnumerable().IgnoreIfEmpty().Sum(); 当我看到GetEnumerable().IgnoreIfEmpty().Sum();时,我期待发生什么GetEnumerable().IgnoreIfEmpty().Sum(); ? In this case, it makes sense. 在这种情况下,它是有道理的。

Note that with C# 6 we can use the following syntax: GetEnumerable()?.Sum() which returns an int? 请注意,使用C#6,我们可以使用以下语法: GetEnumerable()?.Sum()返回一个int? . You could write GetEnumerable()?.Sum() ?? 0 你可以写GetEnumerable()?.Sum() ?? 0 GetEnumerable()?.Sum() ?? 0 or GetEnumerable()?.Sum().GetValueOrDefault() to get a non-null integer that will default to zero. GetEnumerable()?.Sum() ?? 0GetEnumerable()?.Sum().GetValueOrDefault()获取一个默认为零的非空整数。

If you are truly concerned with performance, you could also slightly refactor your method so that it's not an enumerator. 如果你真的关心性能,你也可以稍微重构一下你的方法,这样它就不是一个枚举器。 This may increase the chance of inlining, although I have no idea of the 'arcane' logic of the JIT compiler: 虽然我不知道JIT编译器的“神秘”逻辑,但这可能会增加内联的机会:

public static IEnumerable<T> IgnoreIfEmpty<T>(this IEnumerable<T> enumerable)
{
    if (enumerable == null) return Enumerable.Empty<T>();
    return enumerable;
}

More generally about extending Linq, I think it is perfectly fine as long as the code makes sense. 更一般地说,扩展Linq,我认为只要代码有意义就完全没问题。 MSDN even has an article about it. MSDN甚至有一篇关于它的文章 If you look at the standard Where , Select methods in Linq, and forget about the performance optimizations they have in there, the methods are all mostly one-liner methods. 如果你看一下Linq中的标准WhereSelect方法,并忘记它们在那里的性能优化,这些方法都是一线方法。

You can skip the additional extension method and use null coalescing operator - this is what it's for, and a one-time check for nullability should be a lot more efficient than another state machine: 您可以跳过其他扩展方法并使用null合并运算符 - 这就是它的用途,并且一次性检查可空性应该比另一个状态机更有效:

IEnumerable<int> enumerable = GetEnumerable();
int sum = 0;

sum = (enumerable ?? Enumerable.Empty<int>()).Sum();

Most of the times we write a lot of code just because we are enchanted by the beauty of our creation - not because we really need it - and then we call it abstraction, reusability, extensibility, etc.. 大多数时候我们写了很多代码只是因为我们被创造的美丽所迷惑 - 不是因为我们真的需要它 - 然后我们称之为抽象,可重用性,可扩展性等。

Is this raw piece less readable or less extensible or less reuseable or slower : 这个原始部分是否具有较低的可读性或较低的可扩展性或较少的可重用性或较慢:

var sum = GetEnumerable().Where(a => a != null).Sum(); 

The less code you write - the less code you test - keep it simple. 您编写的代码越少 - 您测试的代码越少 - 保持简单。 BTW - it is good to write extension methods if you can justify it. 顺便说一句 - 如果可以证明它是合理的,那么编写扩展方法是很好的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM