任何IEnumerable的C#EmptyIfFull扩展返回空的派生类型

任何IEnumerable的C#EmptyIfFull扩展返回空的派生类型,c#,C#,假设null和空集合是等价的,我正在尝试为IEnumerable类型编写一个扩展方法,以返回派生类型的空集合,而不是null。这样我就不必到处重复null检查,也不必强制转换IEnumerable back e、 g List methodReturningWillist() { ... } Foo[]方法返回fooarray() { ... } 空条() { List List=MethodReturning愚人(); Foo[]arr=MethodReturningFooArray().Em

假设null和空集合是等价的,我正在尝试为IEnumerable类型编写一个扩展方法,以返回派生类型的空集合,而不是null。这样我就不必到处重复null检查,也不必强制转换IEnumerable back

e、 g

List methodReturningWillist()
{
...
}
Foo[]方法返回fooarray()
{
...
}
空条()
{
List List=MethodReturning愚人();
Foo[]arr=MethodReturningFooArray().EmptyIfNull();
}
公共静态类扩展
{
公共静态T EmptyIfNull(此T iEnumerable)
其中T:IEnumerable,new()
{
var newTypeFunc=Expression.Lambda(Expression.New(typeof(T))).Compile();
返回iEnumerable==null?newTypeFunc():iEnumerable;
}
}

这个扩展似乎很有效,但有人看到任何陷阱吗?

我应该这样改进它

public static class Extension 
{ 
    public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> iEnumerable) 
    { 
        return iEnumerable ?? Enumerable.Empty<T>();
    }
}
公共静态类扩展
{ 
公共静态IEnumerable EmptyIfNull(此IEnumerable IEnumerable)
{ 
返回iEnumerable??Enumerable.Empty();
}
}
或者用C#6更好

公共静态类扩展
{ 
公共静态IEnumerable EmptyIfNull(此IEnumerable IEnumerable)
=>iEnumerable??Enumerable.Empty();
}

是的,在这种情况下它会断开:

IEnumerable<int> test = null;
var result = test.EmptyIfNull();
IEnumerable测试=null;
var结果=test.EmptyIfNull();
您可以这样解决它:

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>();
    }
}
公共静态类扩展
{
公共静态列表EmptyIfFull(此列表)
{
返回列表??新列表();
}
公共静态T[]emptyIfFull(此T[]arr)
{
返回arr??新T[0];
}
公共静态IEnumerable EmptyIfNull(此IEnumerable可枚举)
{   
返回enumerable??enumerable.Empty();
}
}
您需要重载以确保返回的集合类型与以前相同

下面是一个无法通过返回相同集合类型工作的示例:

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();
公共抽象类MyAbstractClass:IEnumerable
{
私有列表模板列表=新列表();
公共IEnumerator GetEnumerator()
{
返回templast.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
返回templast.GetEnumerator();
}
}
MyAbstractClass myClass=null;
MyAbstractClass实例=myClass.EmptyIfNull();
在不知道子类的情况下,我们不可能在这里返回
MyAbstractClass
。对于空引用,如果没有猜测,这是不可能的。此外,当类没有默认构造函数时会发生什么?进入危险区域


您需要一个catch-all
IEnumerable
返回,并让用户强制转换它,或者提供重载,如我在上面所示,一个获得泛型参数的方法(限制为类,因为我将创建此类型的实例)。然后在运行时定义一个默认对象,并检查参数接收到的对象是否为null,如果该对象为null,则返回在运行时实例化的对象,否则返回参数传递的对象本身

我希望这有帮助

    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;
    }
公共静态T EmptyIfNull(此T对象),其中T:class
{           
var objNotNull=Activator.CreateInstance(typeof(T));
if(obj==null)
将objNotNull返回为T;
返回obj;
}
单元测试:


IEnumerable和IEnumerable不是一回事。每次调用
EmptyIfNull
时,您都在编译一个新方法。您可以简单地执行
返回iEnumerable==null?Activator.CreateInstance():iEnumerable出于性能原因,我避免使用Activator。编译后的方法可以缓存,但这部分不是主要关注的问题。我意识到这是一个老问题,但我最近被ping了一下。我注意到现在您添加了:new()约束,该方法可以是
returnIEnumerable??new T()
@Rob我当时没有意识到这一点。我相信这应该是答案(没有测试过)。如果变量为空,代码不会中断,他会先检查变量是否为空。@AlbertoMonteiro不,它中断是因为类型是
IEnumerable
,无法构造是的,忘记了这个细节。这是最好的方法,回答很好Rob@罗布明白了。。。这对我的需求没关系——只要它事先告诉你。请注意,正如所示,这段代码与OP的代码非常不同——它只适用于
IEnumerable
(这确实是大多数代码应该使用的),但对于文章中显示的保留集合类型的测试用例,它失败了(这是否是个好主意可以单独讨论)。添加一些解释和答案,说明此答案如何帮助OP解决当前问题
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();
    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;
    }