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<string> { "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<T> AllOrDefaultML<T>(this IEnumerable<T> items)
{
if (items.IsNullML()) return new List<T>();
return items;
}
public static IEnumerable<T> AllOrDefaultML<T>(this IEnumerable<T> items, Func<T, bool> predicate)
{
items = items.AllOrDefaultML();
if (!items.AnyML()) return new List<T>();
return items.Where(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;
}
public static bool IsNullML(this object item)
{
return item == null;
}
public static bool IsNullOrEmptyML<T>(this IEnumerable<T> items)
{
return !items.AnyML();
}
public static bool AnyML<T>(this IEnumerable<T> items)
{
if (items.IsNullML()) return false;
return items.Count() > 0;
}
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;
}
}
Essentially with these methods I can write the above code as the following:
var items = new List<string> { "apple", "dog", "chair" };
foreach (var item in items.AllOrDefaultML())
{
Console.WriteLine(item);
}
Or even integrate the Each() method:
var items = new List<string> { "apple", "dog", "chair" };
items.AllOrDefaultML().EachML(i => Console.WriteLine(i));
I'm not sure how I feel about the above, but you get the idea.
I've also added a predicate to AllOrDefaultML() which works if the sequence is null:
foreach (var item in items.AllOrDefaultML(i => i.Length == 5))
{
Console.WriteLine(item);
}
Linq has a FirstOrDefault() method, why not have an AllOrDefault() which returns you an empty sequence if it's null and doesn't have any items?
I'm hoping to get some insight in to the question above, and also wondering if anyone else has implemented anything like this before? Has it cleaned up your code much? Is it easier to read? Is it confusing for colleagues (particularly new start) who aren't totally aware of what's happening? Is there anything fundamentally wrong with what I have here?
if (xs.Any()) foreach (var x in xs)
though ... \$\endgroup\$