Jon Wong Jon Wong - 19 days ago 7
C# Question

C# EmptyIfNull extension for any IEnumerable to return empty derived type

Assuming nulls and empty collections are equivalent, I'm trying to write an extension method for IEnumerable types to return empty collections of the derived type instead of null. This way I don't have to repeat null checks all over the place, and I don't get an IEnumerable back that I have to cast.

e.g.

List<Foo> MethodReturningFooList()
{
...
}

Foo[] MethodReturningFooArray()
{
...
}

void Bar()
{
List<Foo> list = MethodReturningFooList().EmptyIfNull();
Foo[] arr = MethodReturningFooArray().EmptyIfNull();
}

public static class Extension
{
public static T EmptyIfNull<T>(this T iEnumerable)
where T : IEnumerable, new()
{
var newTypeFunc = Expression.Lambda<Func<T>>(Expression.New(typeof(T))).Compile();
return iEnumerable == null ? newTypeFunc() : iEnumerable;
}
}


This extension seems to work, but does anyone see any pitfalls?

Rob Rob
Answer

Yes, it will break in this case:

IEnumerable<int> test = null;
var result = test.EmptyIfNull();

You can solve it like this:

public static class Extension
{
    public static List<T> EmptyIfNull<T>(this List<T> list)
    {
        return list ?? new List<T>();
    }
    public static T[] EmptyIfNull<T>(this T[] arr)
    {
        return arr ?? new T[0];
    }
    public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> enumerable)
    {   
        return enumerable ?? Enumerable.Empty<T>();
    }
}

You'll need overloads to make sure you're returning the same collection type (as you were before).

Here's an example of a case that can't work by returning the same collection type:

public abstract class MyAbstractClass : IEnumerable<int>
{
    private List<int> tempList = new List<int>();
    public IEnumerator GetEnumerator()
    {
        return tempList.GetEnumerator();
    }
    IEnumerator<int> IEnumerable<int>.GetEnumerator()
    {
        return tempList.GetEnumerator();
    }
}

MyAbstractClass myClass = null;
MyAbstractClass instance = myClass.EmptyIfNull();

There's no way we can return a MyAbstractClass here without knowing about subclasses. And with a null reference, that's not possible without guessing. Further, what happens when classes do not have a default constructor? Getting into dangerous territory.

You'll need to either have a catch-all IEnumerable<T> return, and have the user cast it, or provide overloads as I've shown above

Comments