.net 可枚举。任何<;T>;(这个IEnumerable<;T>;源)当然也应该处理空枚举?

.net 可枚举。任何<;T>;(这个IEnumerable<;T>;源)当然也应该处理空枚举?,.net,linq,.net,Linq,Any()方法对空枚举值抛出ArgumentNullException,是否有一些迂腐的基于枚举器理论的原因 我尽可能使用IEnumerable来传递数据集合,但在大多数情况下,我会使用这种代码: public class Foo { public IEnumerable<IBar> Bars { get; set; } } ////---- public static void UseAFoo(Foo foo) { //if Bars has something in

Any()方法对空枚举值抛出
ArgumentNullException
,是否有一些迂腐的基于枚举器理论的原因

我尽可能使用IEnumerable来传递数据集合,但在大多数情况下,我会使用这种代码:

public class Foo
{
  public IEnumerable<IBar> Bars { get; set; }
}

////----

public static void UseAFoo(Foo foo)
{
  //if Bars has something in it, then do something:
  if(foo.Bars != null && foo.Bars.Any())
  {
    //blah
  }
}
公共类Foo
{
公共IEnumerable条{get;set;}
}
////----
公共静态无效UseAFoo(Foo-Foo)
{
//如果酒吧里有东西,那就做点什么:
if(foo.bar!=null&&foo.bar.Any())
{
//废话
}
}
或者

if((foo.bar??Enumerable.Empty()).Any())
{
//啊!
}
或修改属性声明,如下所示:

public class Foo
{
  private IEnumerable<IBar> _bars;
  public IEnumerable<IBar> Bars
  { 
    get { return _bars ?? Enumerable.Empty<IBar>(); }
    set { _bars = value; }
  }
}
公共类Foo
{
私人可数酒吧;
公共可数酒吧
{ 
获取{返回_条??可枚举的.Empty();}
设置{u条=值;}
}
}
或“更高效”的方式:

get { return _bars; }
set { _bars = value ?? Enumerable.Empty<IBar>(); }
get{return\u bar;}
设置{u bar=value??可枚举的.Empty();}
在任何情况下,这都是令人讨厌的。好吧,我可以做我自己的扩展方法(我已经有了)——但是命名有点棘手,因为最好的名字已经被取了!我喜欢
NotNullAndAny()
,但我不喜欢它

Any()
(以及它的委托驱动兄弟)在我看来,如果枚举值为null,则应该返回false-它不应该抛出
ArgumentNullException

但是,就像我一开始说的,这里有没有我遗漏的隐藏合同?或者只是迂腐;)

更新

我有一种感觉,大家的共识是,所有linq扩展都应该抛出
ArgumentNullException
,即使是
Any()
——老实说,尽管我觉得
Any()
仍然可以这样做,的参数不够有说服力,无法克服这样一个基本事实:如果枚举值为null,则无法令人满意地完成它的常规操作,因此必须抛出异常

因此,我将使用一个额外的扩展方法
NotNullAndAny()
,来清理可能出现空值(而不是错误)的代码。在不允许空枚举的情况下,我也会抛出
ArgumentNullException
。但是,在某些情况下,更实际的做法是允许代码的使用者保留未设置的属性,同时不强制我的类需要手动实现的属性或默认构造函数,以确保这些属性永远不会得到
null
。如果这是我的选择,那么我委托自己的代码必须同时检查null和empty


现在唯一的问题是,对马克的回答是什么!:)

我认为当参数为
null
时,它不应该返回
false

这个函数为
null
参数抛出异常,与其他
BCL
类的一般行为相匹配

序列
空时,它确实返回
false


具有
null
值和将参数值作为
空序列
(这是一个没有元素的序列)是有区别的。

我认为当参数为
null
时,它不应该返回
false

这个函数为
null
参数抛出异常,与其他
BCL
类的一般行为相匹配

序列
空时,它确实返回
false


有一个
null
值和有一个参数值作为
空序列
(这是一个没有任何元素的序列)是有区别的。

我无法回答是否有合理的理论原因,但对我来说,我们处理的是两个不同的概念。我将误用语言,讨论列表,而不是枚举,但同样的论点应该适用:列表不包含任何项与列表根本不存在之间有很大区别


就个人而言,考虑到您列出的代码选项,我会选择一个您没有列出的选项(如果有一个可设置的可枚举项是有意义的,并且您的类确实可以处理任何可枚举项):

公共类Foo
{
private IEnumerable_bars=可枚举的.Empty();;
公共可数酒吧
{ 
获取{返回_条;}
设置{
如果(value==null)抛出新的ArgumentNullException(“value”);
_条=值;
}
}
}

i、 e.如果您不想处理空值,请不要让空值潜入您的设计中

我无法回答是否有合理的理论原因,但对我来说,我们正在处理两个不同的概念。我将误用语言,讨论列表,而不是枚举,但同样的论点应该适用:列表不包含任何项与列表根本不存在之间有很大区别


就个人而言,考虑到您列出的代码选项,我会选择一个您没有列出的选项(如果有一个可设置的可枚举项是有意义的,并且您的类确实可以处理任何可枚举项):

公共类Foo
{
private IEnumerable_bars=可枚举的.Empty();;
公共可数酒吧
{ 
获取{返回_条;}
设置{
如果(value==null)抛出新的ArgumentNullException(“value”);
_条=值;
}
}
}

i、 e.如果您不想处理空值,请不要让空值潜入您的设计中。

空值在实践中通常是一种不正确的状态,并且被绝大多数API视为不正确的状态-linq的API遵循这一点。也许您应该首先质疑为什么有空枚举,并对此加以防范。第二种方法是编写自己的扩展方法,为您提供所需的行为。过多的空检查(代码内部而不仅仅是参数检查)通常是一种代码气味

Null在实践中经常被忽略
get { return _bars; }
set { _bars = value ?? Enumerable.Empty<IBar>(); }
public class Foo
{
  private IEnumerable<IBar> _bars = Enumerable.Empty<IBar>();;
  public IEnumerable<IBar> Bars
  { 
    get { return _bars;  }
    set {
        if(value==null) throw new ArgumentNullException("value");
         _bars = value;
    }
  }
}