HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

IEnumerable Extensions Linq AllOrDefault() Each()

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
linqeachextensionsallordefaultienumerable

Problem

I recently decided to try and write my own implementation of Linq, which then lead me on to trying to solve some of the problems we have in our code base at work. Our code is littered with the following (obviously a simplified example):

var items = new List { "apple", "dog", "chair" };

        if (items != null && items.Any())
        {
            foreach (var item in items)
            {
                Console.WriteLine(item);
            }
        }


Essentially each time we are iterating over a collection we check to make sure the collection is not null and it actually contains at least one item.

I decided to write the following extension methods to alleviate this mess (the ML suffix on the methods was to differentiate from Linq [mostly for testing]):

```
public static class EnumerableExtensions
{
public static IEnumerable AllOrDefaultML(this IEnumerable items)
{
if (items.IsNullML()) return new List();

return items;
}

public static IEnumerable AllOrDefaultML(this IEnumerable items, Func predicate)
{
items = items.AllOrDefaultML();

if (!items.AnyML()) return new List();

return items.Where(predicate);
}

public static IEnumerable EachML(this IEnumerable items, Action fn)
{
var e = items.GetEnumerator();
while (e.MoveNext())
{
fn(e.Current);
}
return items;
}

public static bool IsNullML(this object item)
{
return item == null;
}

public static bool IsNullOrEmptyML(this IEnumerable items)
{
return !items.AnyML();
}

public static bool AnyML(this IEnumerable items)
{
if (items.IsNullML()) return false;

Solution

In those places you use new List(), I would recommend using Enumerable.Empty() in its place. It's a little more idiomatic and, as those empty collections are cached by the runtime, repeated calls don't new up more stuff to be GC'd later.

items.Count() > 0 can be replaced with items.Any(), which is more descriptive of what you're trying to test for, and will likely be cheaper computationally. More specifically,

public static bool AnyML(this IEnumerable items, Func predicate)
{
    if (items == null) return false;

    items = items.Where(predicate);

    return items.Count() > 0;
}


can be replaced with

public static bool AnyML(this IEnumerable items, Func predicate)
{
    if (items == null) return false;

    return items.Any(predicate);
}


EDITED TO ADD

public static IEnumerable EachML(this IEnumerable items, Action fn)
{
    var e = items.GetEnumerator();
    while (e.MoveNext())
    {
        fn(e.Current);
    }
    return items;
}


has a small bug - items.GetEnumerator() returns an IEnumerator, which implements IDisposable. This means you should Dispose() of it when you're done using it (see https://stackoverflow.com/a/232616/3312 for good info). Write it like this to do so:

using (var e = items.GetEnumerator())
    {
        while (e.MoveNext())
        {
            fn(e.Current);
        }
    }
    return items;
}

Code Snippets

public static bool AnyML<T>(this IEnumerable<T> items, Func<T, bool> predicate)
{
    if (items == null) return false;

    items = items.Where(predicate);

    return items.Count() > 0;
}
public static bool AnyML<T>(this IEnumerable<T> items, Func<T, bool> predicate)
{
    if (items == null) return false;

    return items.Any(predicate);
}
public static IEnumerable<T> EachML<T>(this IEnumerable<T> items, Action<T> fn)
{
    var e = items.GetEnumerator();
    while (e.MoveNext())
    {
        fn(e.Current);
    }
    return items;
}
using (var e = items.GetEnumerator())
    {
        while (e.MoveNext())
        {
            fn(e.Current);
        }
    }
    return items;
}

Context

StackExchange Code Review Q#111120, answer score: 4

Revisions (0)

No revisions yet.