39

While working with Linq extensions it's normal to see code like this:

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.

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?

Update: My target framework is: 3.5

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Mahmoud Samy
  • 2,822
  • 7
  • 34
  • 78
  • 5
    `IgnoreIfEmpty` isn't a great name, since "Ignore" is vague, and an empty enumerable is different from a null one. – Ben Aaronson Feb 24 '16 at 08:39
  • 4
    If you have control over the `GetEnumerable` methods you should really be returning empty collections instead of nulls. – Steve Feb 24 '16 at 08:47
  • @DarrenYoung, I have no control over GetEnumerable. plus null is expected return. – Mahmoud Samy Feb 24 '16 at 08:50
  • 1
    That is not ignoring it if it's empty; it's converting a null to an empty sequence. You should call it `EmptyIfNull()` or something like that. – Matthew Watson Feb 24 '16 at 09:19
  • 2
    I use an EmptyIfNull method like that, it seemed to fit naming style with the DefaultIfEmpty – Mant101 Feb 24 '16 at 09:31
  • Maybe GetEnumerable() would return an empty enumerable insted of null [http://stackoverflow.com/questions/1969993/is-it-better-to-return-null-or-empty-collection](http://stackoverflow.com/questions/1969993/is-it-better-to-return-null-or-empty-collection) – Nacho Feb 24 '16 at 10:09
  • 1
    @msa I'd argue returning null in place of an enumerable is unexpected. MS guidelines agree https://msdn.microsoft.com/en-us/library/dn169389(v=vs.110).aspx – Andy Feb 25 '16 at 02:10
  • http://stackoverflow.com/a/11267819/8155 – Amy B Mar 20 '16 at 17:11

4 Answers4

40

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?

In your question you state:

While working with Linq extensions it's normal to see code like this (insert enumerable null check here)

And I beg to differ. The common practice says don't return null where an IEnumerable<T> is expected. Most cases should return an empty collection (or IEnumerable), leaving null to the exceptional, because null is not empty. This would make your method entirely redundant. Use Enumerable.Empty<T> where needed.

Community
  • 1
  • 1
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • 11
    Definitely agree with preferring empty collections over null. Null should be used sparingly and with clarity, for anything, not just collections. Lost count of how many bugs come from null references. – Adam Houldsworth Feb 24 '16 at 08:39
29
  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();? In this case, it makes sense.

Note that with C# 6 we can use the following syntax: GetEnumerable()?.Sum() which returns an int?. You could write GetEnumerable()?.Sum() ?? 0 or GetEnumerable()?.Sum().GetValueOrDefault() to get a non-null integer that will default to zero.

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:

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. MSDN even has an article about it. 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.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Bas
  • 26,772
  • 8
  • 53
  • 86
  • sorry I forgot to mention that my target framework is 3.5 – Mahmoud Samy Feb 24 '16 at 08:37
  • 1
    @Tyress, why wouldn't it work? The return type is the exact same type as the parameter type. Did you still have a `yield break` statement in there? – Bas Feb 24 '16 at 08:38
  • 1
    @msa Everything in the answer should apply to .NET 3.5, except the C# 6 side note. Note that you may want to upgrade your toolset, it's almost 10 years old! – Bas Feb 24 '16 at 08:40
  • 2
    Actually I think null conditional operator should work fine on .NET 3.5 as long as your compiler is C#6 compatible: http://stackoverflow.com/questions/28921701/does-c-sharp-6-0-work-for-net-4-0 – Ben Aaronson Feb 24 '16 at 08:42
  • @Bas oh, yes I did have the yield break statement. Awesome. – Tyress Feb 24 '16 at 08:45
  • @Bas, unfortunately it's a legacy project. we are rewriting it now from scratch but we have to maintain the old one – Mahmoud Samy Feb 24 '16 at 08:54
  • @msa What version of Visual Studio are you using to compile the project? If it's Visual Studio 2010 or later, you are already using a later version of the C# language than was released with .Net 3.5 – Matthew Watson Feb 24 '16 at 09:15
  • 1
    And if you write the code like this, then you don't really need the extension method because the caller code can also be reduced to a one-liner: `int sum = (enumerable ?? Enumerable.Empty()).Sum()`. – CompuChip Feb 24 '16 at 09:22
14

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:

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

sum = (enumerable ?? Enumerable.Empty<int>()).Sum();
w.b
  • 11,026
  • 5
  • 30
  • 49
-1

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.

AkshayM
  • 317
  • 6
  • 18
  • 1
    I agree with your statements on keeping things simple to some degree, but the code you give does something very different than the code of the question: You check for elements being `null`, while the questions checks if the enumerable itself is `null`. – amulware Mar 02 '16 at 07:56
  • @amulware enumerable should not be null - if it is null then the developer is breaking standard practices. Return empty collections - not null. If enumerable is null then return - why compute ? Ref: https://msdn.microsoft.com/en-us/library/dn169389(v=vs.110).aspx – AkshayM Mar 03 '16 at 10:40
  • Why is my answer downvoted - any one in specific having any specific complains ? Or even am free to downvote all responses ? – AkshayM Mar 03 '16 at 10:41