简体   繁体   中英

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.

eg

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?

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

I just should improve this like that

public static class Extension 
{ 
    public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> iEnumerable) 
    { 
        return iEnumerable ?? Enumerable.Empty<T>();
    }
}

Or better with C# 6

public static class Extension 
{ 
    public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> iEnumerable) 
        => iEnumerable ?? Enumerable.Empty<T>();
}

A method that I get a generic parameter (with the restriction of being a class because I will create a instance of this type). Then I define a default object at runtime, and check if the object received by parameter is null, if the object is null I return the object that was instantiated at runtime otherwise return the object itself passed by parameter.

I hope that helps

    public static T EmptyIfNull<T>(this T obj) where T : class
    {           
        var objNotNull = Activator.CreateInstance(typeof(T));

        if (obj == null)
            return objNotNull as T;

        return obj;
    }

Unit Test :

在此输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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