是否有一些迂腐的基于枚举器理论的理由为什么Any()方法为null枚举抛出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
}
}
或者,
if((foo.Bars ?? Enumerable.Empty<IBar>()).Any())
{
//ugh!
}
或者修改属性声明:
public class Foo
{
private IEnumerable<IBar> _bars;
public IEnumerable<IBar> Bars
{
get { return _bars ?? Enumerable.Empty<IBar>(); }
set { _bars = value; }
}
}
或“更有效”的方式:
get { return _bars; }
set { _bars = value ?? Enumerable.Empty<IBar>(); }
在每种情况下 - 这都是令人讨厌的。好吧所以我可以做我自己的扩展方法(我有) - 但是命名有点棘手,因为最好的名字已经被采用了!我去了NotNullAndAny()
,但我不喜欢它。
Any()
(以及代理驱动的兄弟)应该返回false - 它不应该抛出ArgumentNullException
。
但是就像我在开始时所说的那样 - 这里有一些隐藏的合同我错过了吗?或者只是迂腐;)
更新
我有一种感觉,即所有linq扩展应该抛出ArgumentNullException
,即使是Any()
的情况 - 并且说实话,我觉得案件仍然可以对于Any()
,参数不具有说服力,足以克服这样一个基本事实,即如果一个枚举为空,就不能令人满意地完成一般操作,因此必须抛出异常。
因此,我将使用额外的扩展方法NotNullAndAny()
来清理我的代码,其中空值是合理可能的(而不是错误)。如果不允许null枚举,我也已经抛出了ArgumentNullException
。但是在某些情况下,允许我的代码的使用者保留未设置的属性更为实际,同时不要强迫我的类需要手动实现的属性或默认构造函数来确保这些属性不会得到null
。如果这是我的选择,那么我委托我自己的代码来检查null和empty。
现在唯一的事情就是回答标记作为答案! :)
答案 0 :(得分:2)
我认为当参数为false
时,它不应返回null
。
使用此null
参数抛出异常与其他BCL
类的一般行为相匹配。
当false
为sequence
时,它会返回empty
。
将null
值与参数值作为empty sequence
(其中没有元素的序列)之间存在差异。
答案 1 :(得分:2)
我无法回答它是否有合理的理论原因 - 但对我来说,我们正在处理两个截然不同的概念。我会滥用语言并谈论列表,而不是枚举,但同样的论点应该适用:存在一个不包含任何项目的列表与根本不存在的列表之间存在很大差异。
就个人而言,鉴于您列出的代码选项,我会选择一个您没有列出的代码(如果有可设置的可枚举,并且您的类真的可以使用任何可枚举的代码):
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;
}
}
}
即。如果您不想处理它们,请不要让空值进入您的设计
答案 2 :(得分:1)
在实践中,Null通常是一个不正确的状态,绝大多数API都会这样做 - linq的API就是这样。也许你应该首先质疑为什么你有空的枚举,并防范这一点。第二种方法是编写自己的扩展方法,为您提供所需的行为。过多的空检查(内部代码而不仅仅是参数检查)通常是代码味道。