返回null或空集合更好吗?


420

这是一个普遍的问题(但是我正在使用C#),最好的方法是什么(最佳实践),对于以集合为返回类型的方法,您是否返回null或空集合?



3
@CPerkins-是的,有。Microsoft自己的.NET Framework设计指南中明确说明了这一点。有关详细信息,请参见RichardOD的答案。
格雷格·比奇

51
仅当含义是“我无法计算结果”时,才应返回null。空值永远不应具有“空”的语义,而只能具有“缺失”或“未知”的语义。:在我的受检物品的更多细节blogs.msdn.com/ericlippert/archive/2009/05/14/...
埃里克利珀

3
博zh:“任何”都是很多语言。那么,Common Lisp怎么办,其中空列表恰好等于空值?:-)
肯(Ken)

9
实际上,“重复项”是关于返回对象而不是集合的方法的。这是具有不同答案的不同场景。
GalacticCowboy

Answers:


499

空集合。总是。

这很烂:

if(myInstance.CollectionProperty != null)
{
  foreach(var item in myInstance.CollectionProperty)
    /* arrgh */
}

null在返回集合或可枚举的对象时,永远不要返回是一种最佳实践。 总是返回一个空的可枚举/集合。它可以防止上述的废话,并防止您的汽车被班上的同事和用户所困扰。

在谈论属性时,请始终设置一次属性,然后忘记它

public List<Foo> Foos {public get; private set;}

public Bar() { Foos = new List<Foo>(); }

在.NET 4.6.1中,您可以将其压缩得很多:

public List<Foo> Foos { get; } = new List<Foo>();

当谈论返回枚举的方法时,您可以轻松地返回一个空的枚举而不是null...

public IEnumerable<Foo> GetMyFoos()
{
  return InnerGetFoos() ?? Enumerable.Empty<Foo>();
}

Enumerable.Empty<T>()与返回例如新的空集合或数组相比,使用效率更高。


24
我同意威尔,但我认为“总是”有点过分。空集合可能意味着“ 0个项目”,而返回Null可能意味着“完全没有集合”-例如。如果您正在解析HTML,则查找具有id =“ foo”的<ul>,<ul id =“ foo”> </ ul>可能返回空集合;如果没有<ul>且id =“ foo”,则返回null会更好(除非您想例外处理此情况)
Patonza,2009年

30
这并不总是一个问题,即“您是否可以轻松地返回一个空数组”,而是一个空数组在当前上下文中是否会引起误解。空数组实际上意味着某些东西,null也是如此。说应该总是返回一个空数组而不是null,这与说布尔方法应该总是返回true一样被误导。这两个可能的值都传达了含义。
David Hedlund

31
实际上,您应该更喜欢返回System.Linq.Enumerable.Empty <Foo>(),而不是返回新的Foo [0]。它更加明确,可以为您节省一次内存分配(至少在我安装的.NET实现中)。
Trillian

4
@Will:OP:“您是否将具有集合作为返回类型的方法返回null或空集合”。我认为,在这种情况下,无论是IEnumerableICollection不管那么多了。无论如何,如果您选择某种类型的东西,ICollection它们也会返回null...我希望他们返回一个空集合,但是我遇到了他们的returning null,所以我想在这里只提一下。我会说一个可枚举集合的默认值为空而不是null。我不知道这是一个敏感的话题。
Matthijs Wessels,


154

根据《框架设计指南第二版》(第256页):

不要从集合属性或返回集合的方法中返回空值。而是返回一个空集合或一个空数组。

这是另一篇有趣的文章,介绍了不返回空值的好处(我试图在Brad Abram的博客中找到某些内容,并且他链接到该文章)。

编辑-正如埃里克·利珀特(Eric Lippert)现在评论原始问题时,我也想链接到他的出色文章


33
+1。除非您有非常充分的理由不这样做,否则最好始终遵循框架设计准则。

@Will,绝对。这就是我所遵循的。我从来没有发现需要这样做
RichardOD

是的,这就是您所遵循的。但是,如果您所依赖的API没有遵循它,您就会被“困住”;)
Bozho

@ Bozho-是的。您的答案为这些附带情况提供了一些不错的答案。
RichardOD

90

取决于您的合同具体情况。通常,最好返回空集合,但有时(很少):

  • null 可能意味着更具体的内容;
  • 您的API(合同)可能会迫使您返回null

一些具体的例子:

  • 一个UI组件(来自控件之外的库),如果传递了一个空集合,则可能正在呈现一个空表,如果传递了null,则可能根本不呈现任何表。
  • 在Object-to-XML(JSON / whatever)中,null这意味着元素丢失,而空集合将导致冗余(可能不正确)<collection />
  • 您正在使用或实现一个API,该API明确指出应返回/传递null

4
@ Bozho-这里有一些有趣的例子。
RichardOD

1
尽管我不认为您应该围绕其他错误构建代码,并且按照定义,c#中的“ null” 从不表示特定含义,但我还是会看到您的观点。null值定义为“无信息”,因此说它携带特定信息是oximoron。因此,.NET指南指出,如果集合确实为空,则应返回一个空集合。返回null的意思是:“我不知道预期的组合去了哪里”
Rune FS

13
不,它的意思是“没有集合”而不是“集合没有元素”
Bozho

我曾经相信,然后我不得不编写许多TSQL,并得知情况并非总是如此,嘿嘿。

1
包含Object-To-Xml的部分在
Mihai Caracostea

36

还有一点尚未提及。考虑以下代码:

    public static IEnumerable<string> GetFavoriteEmoSongs()
    {
        yield break;
    }

调用此方法时,C#语言将返回一个空的枚举数。因此,为了与语言设计保持一致(并因此符合程序员的期望),应返回一个空集合。


1
从技术上讲,我不认为这会返回空集合。
FryGuy

3
@FryGuy-不,不是。它返回一个Enumerable对象,该对象的GetEnumerator()方法返回一个Enumerator,该Enumerator(通过带有集合的aaragy)为空。也就是说,枚举数的MoveNext()方法始终返回false。调用示例方法不会返回null,也不会返回其GetEnumerator()方法返回null的Enumerable对象。
Jeffrey L Whitledge,09年

31

空的对消费者更友好。

有一种明确的方法可以构成一个空的可枚举的对象:

Enumerable.Empty<Element>()

2
感谢您的巧妙技巧。我像一个白痴一样实例化了一个空的List <T>(),但这看起来更干净了,效率也可能更高一些。
Jacobs Data Solutions 2010年

18

在我看来,无论上下文如何,您都应该返回在语义上正确的值。一条说“总是返回空集合”的规则对我来说似乎有点简单。

假设在一个医院系统中,我们有一个函数应该返回过去5年中所有以前住院的清单。如果客户没有去过医院,则返回空列表是很有意义的。但是,如果客户将准入表格的这一部分留空,该怎么办?我们需要一个不同的值来区分“空列表”与“无答案”或“不知道”。我们可以抛出异常,但这不一定是错误条件,也不一定会使我们脱离正常的程序流程。

我经常对无法区分零答案和无答案的系统感到沮丧。我曾多次要求系统输入一些数字,然后输入零,然后收到一条错误消息,告诉我必须在该字段中输入一个值。我只是做:我输入了零!但是它不会接受零,因为它无法将它与没有答案区分开。


回复桑德斯:

是的,我假设“人员未回答问题”与“答案为零”之间存在差异。这就是我回答的最后一段的重点。许多程序无法区分“不知道”与空白还是零,在我看来,这是一个潜在的严重缺陷。例如,大约一年前,我正在买房。我去了一个房地产网站,列出了许多要价为0美元的房屋。对我来说听起来不错:他们免费提供这些房屋!但是我确信可悲的现实是他们还没有输入价格。在这种情况下,您可能会说:“好吧,零显然意味着他们没有输入价格-没有人会免费提供房屋。” 但是该网站还列出了各个城镇房屋的平均要价和售价。我不禁要问,平均值是否不包含零,因此某些地方的平均值不正确。即,平均$ 100,000是多少?$ 120,000;和“不知道”?从技术上讲,答案是“不知道”。我们可能真正希望看到的是110,000美元。但是我们可能会得到的是73,333美元,这是完全错误的。另外,如果我们在用户可以在线订购的网站上遇到此问题,该怎么办?(对于房地产而言,这不太可能,但是我敢肯定,您已经在许多其他产品上看到了这一点。)我们是否真的希望将“未指定价格”解释为“免费”?因此,某些地方的平均值偏低。即,平均$ 100,000是多少?$ 120,000;和“不知道”?从技术上讲,答案是“不知道”。我们可能真正希望看到的是110,000美元。但是我们可能会得到的是73,333美元,这是完全错误的。另外,如果我们在用户可以在线订购的网站上遇到此问题,该怎么办?(对于房地产而言,这不太可能,但是我敢肯定,您已经在许多其他产品上看到了这一点。)我们是否真的希望将“未指定价格”解释为“免费”?因此,某些地方的平均值偏低。即,平均$ 100,000是多少?$ 120,000;和“不知道”?从技术上讲,答案是“不知道”。我们可能真正希望看到的是110,000美元。但是我们可能会得到的是73,333美元,这是完全错误的。另外,如果我们在用户可以在线订购的网站上遇到此问题,该怎么办?(对于房地产而言,这不太可能,但是我敢肯定,您已经在许多其他产品上看到了这一点。)我们是否真的希望将“未指定价格”解释为“免费”?这是完全错误的。另外,如果我们在用户可以在线订购的网站上遇到此问题,该怎么办?(对于房地产而言,这不太可能,但是我敢肯定,您已经在许多其他产品上看到了这一点。)我们是否真的希望将“未指定价格”解释为“免费”?这是完全错误的。另外,如果我们在用户可以在线订购的网站上遇到此问题,该怎么办?(对于房地产而言,这不太可能,但是我敢肯定,您已经在许多其他产品上看到了这一点。)我们是否真的希望将“未指定价格”解释为“免费”?

RE具有两个独立的功能,“有吗?” 还有“如果是的话,那是什么?” 是的,您当然可以这样做,但是为什么要这么做?现在,调用程序必须进行两次调用,而不是一次。如果程序员未能调用“ any”会发生什么?然后直接进入“这是什么?” ?程序会返回误导性的零吗?抛出异常?返回未定义的值?它创建更多的代码,更多的工作和更多的潜在错误。

我看到的唯一好处是,它使您能够遵守任意规则。该规则是否有任何优势值得遵循呢?如果没有,何必呢?


回复果酱蛋糕:

考虑一下实际的代码是什么样子。我知道问题说的是C#,但是如果我写Java,请原谅。我的C#不是很敏锐,原理是一样的。

返回空值:

HospList list=patient.getHospitalizationList(patientId);
if (list==null)
{
   // ... handle missing list ...
}
else
{
  for (HospEntry entry : list)
   //  ... do whatever ...
}

具有单独的功能:

if (patient.hasHospitalizationList(patientId))
{
   // ... handle missing list ...
}
else
{
  HospList=patient.getHospitalizationList(patientId))
  for (HospEntry entry : list)
   // ... do whatever ...
}

实际上,它是一两行或更少的具有null返回值的代码,因此对调用方的负担不大,也就更少。

我看不到它是如何造成DRY问题的。好像我们不必执行两次调用。如果在列表不存在时我们总是想做同样的事情,也许我们可以将处理向下推到get-list函数,而不是让调用者来做,因此将代码放在调用者中将违反DRY。但是,我们几乎肯定不想总是做同样的事情。在必须要处理列表的函数中,缺少列表是一个错误,很可能会中断处理。但是在编辑屏幕上,如果他们还没有输入数据,我们当然不希望停止处理:我们希望让他们输入数据。因此,必须以一种或另一种方式在调用者级别上处理“无列表”。而且,无论我们使用null返回值还是使用单独的函数执行此操作,都不会影响更大的原则。

当然,如果调用者不检查null,则程序可能会因null指针异常而失败。但是,如果有一个单独的“获取任何信息”功能,而调用者没有调用该功能,而是盲目地调用了“获取列表”功能,那么会发生什么呢?如果它抛出异常或其他原因失败,那几乎与如果它返回null并且不检查它会发生什么情况相同。如果它返回一个空列表,那是错误的。您无法区分“我有一个零元素的列表”和“我没有一个列表”。这就像在用户未输入任何价格时返回零价格:这是错误的。

我看不到在集合中附加附加属性有何帮助。呼叫者仍然必须检查它。这比检查null更好吗?同样,可能发生的绝对最糟糕的事情是程序员忘记检查它并给出错误的结果。

如果程序员熟悉null的意思是“没有值”,那么返回null的函数就不足为奇了,我认为任何称职的程序员都应该听说过,无论他认为这是一个好主意。我认为拥有单独的职能更多是“惊喜”问题。如果程序员不熟悉API,则当他在没有数据的情况下运行测试时,他会迅速发现有时他会返回null。但是他怎么会发现另一个功能的存在,除非他想到可能存在这样一个功能并且他检查了文档,并且文档是完整且可理解的?我宁愿有一个总是能给我有意义的响应的函数,而不是两个我必须知道并记得要同时调用的函数。


4
为什么必须在相同的返回值中组合“无答案”和“零”响应?取而代之的是,该方法是否返回“过去五年中任何以前的住院记录”,并有一个单独的方法询问“是否填写过以前的住院记录列表?”。它假定有填充以前没有住院清单之间的差异,以及列表不填充。
约翰·桑德斯

2
但是,如果您返回null,则无论如何都会给调用者带来额外的负担!调用者必须检查每个返回值是否为空-严重违反DRY。如果要分别指示“呼叫者未回答问题”,则创建一个额外的方法调用以指示事实更简单。或者,或者使用具有DeclinedToAnswer额外属性的派生集合类型。永远不返回null的规则根本不是任意的,这是“最少惊奇原则”。同样,方法的返回类型应表示其名称所说明的含义。Null几乎没有。
jammycakes 2012年

2
您假设您的getHospitalizationList仅从一个位置调用,并且/或者它的所有调用者都希望区分“无应答”和“零”两种情况。有的情况,其中呼叫方不需要进行区分,所以你强迫他们在地方,这不应该是必要的补充null检查(几乎可以肯定是多数)。这给您的代码库增加了巨大的风险,因为人们都很容易忘记这样做-并且因为很少有合理的理由返回null而不是集合,所以这种可能性更大。
jammycakes 2012年

3
RE名称:除非函数名称与函数名称一样长,否则函数名称无法完全描述函数的用途,因此极为不切实际。但是无论如何,如果在没有给出答案的情况下该函数返回一个空列表,则出于同样的原因,是否不应该将其称为“ getHospitalizationListOrEmptyListIfNoAnswer”?但是,实际上,您是否坚持认为应该将Java Reader.read函数重命名为readCharacterOrReturnMinusOneOnEndOfStream?该ResultSet.getInt应该真的是“ getIntOrZeroIfValueWasNull”吗?等等
周杰伦

4
RE每个呼叫都想区分:好吧,是的,我假设是,或者至少是呼叫者的作者应该做出有意识的决定,认为他不在意。如果该函数返回“不知道”的空列表,并且调用者盲目地将其视为“无”,则可能会给出严重不准确的结果。假设函数是否为“ getAllergicReactionToMedicationList”。如果一个程序盲目地将“未输入清单”视为“患者没有已知的过敏反应”,则该程序实际上可能导致杀死患者。在许多其他系统中,即使效果不那么出色,您也会得到类似的结果。...
周杰伦

10

如果空集合在语义上有意义,那就是我更愿意返回的内容。返回一个空集合以进行GetMessagesInMyInbox()通信“收件箱中实际上没有任何消息”,而返回null可能对传达足够的数据来说明可能要返回的列表的样子很有用。


6
在您给出的示例中,听起来如果方法不能满足请求而不是仅仅返回null,则该方法可能应该抛出异常。异常对于诊断问题比空值更为有用。
格雷格·比奇

好吧,是的,在收件箱示例中,一个null值肯定看起来不合理,我是在更笼统地考虑那个值。异常也很好地传达了出问题的事实,但是,如果完全希望所指的“数据不足”,则抛出异常将导致设计不佳。我想的是一种完全有可能且该方法有时无法计算响应的完全没有错误的方案。
David Hedlund

6

返回null可能更有效,因为不会创建新对象。但是,它通常也需要null检查(或异常处理)。

从语义上说,null空列表并不意味着同一件事。差异是细微的,在特定情况下,一种选择可能会比另一种更好。

无论您选择哪种方式,都要记录在案,以免造成混淆。


8
在考虑API设计的正确性时,效率几乎永远不会成为一个因素。在某些非常特殊的情况下,例如图形基元,可能是这样,但是当处理列表和大多数其他高级事物时,我非常怀疑。
格雷格·比奇

同意Greg的观点,尤其是考虑到API用户必须编写代码来补偿这种“优化”的效率可能比首先使用更好的设计低得多。
Craig Stuntz 09年

同意,并且在大多数情况下,这根本不值得优化。使用现代内存管理,空列表实际上是免费的。
杰森·贝克


4

视情况而定。如果是特殊情况,则返回null。如果函数恰好返回一个空集合,那么显然返回就可以了。但是,由于参数无效或其他原因而将空集合作为特殊情况返回并不是一个好主意,因为它掩盖了特殊情况。

实际上,在这种情况下,我通常更喜欢抛出一个异常以确保它确实未被忽略:)

说这使代码更加健壮(通过返回一个空集合),因为它们不必处理null条件,这是不好的,因为它只是掩盖了应由调用代码处理的问题。


4

我认为这null与空集合不是一回事,您应该选择最能代表您所返回的东西。在多数情况下null,什么都不是(SQL中除外)。一个空的集合是一个东西,尽管是一个空的东西。

如果您必须选择其中一个,那么我想您应该倾向于一个空集合而不是null。但是有时候空集合与空值不是同一回事。


4

始终考虑使用您的客户端(正在使用您的api):

返回“ null”通常会导致客户端无法正确处理null检查的问题,这会在运行时导致NullPointerException。我曾见过这样的情况:缺少这种空检查会导致优先生产问题(客户端对空值使用foreach(...))。在测试过程中,不会发生问题,因为操作的数据略有不同。


3

我想在这里举例说明。

在这里考虑一个案例。

int totalValue = MySession.ListCustomerAccounts()
                          .FindAll(ac => ac.AccountHead.AccountHeadID 
                                         == accountHead.AccountHeadID)
                          .Sum(account => account.AccountValue);

在这里考虑我正在使用的功能..

1. ListCustomerAccounts() // User Defined
2. FindAll()              // Pre-defined Library Function

我可以轻松使用ListCustomerAccountFindAll而不是。

int totalValue = 0; 
List<CustomerAccounts> custAccounts = ListCustomerAccounts();
if(custAccounts !=null ){
  List<CustomerAccounts> custAccountsFiltered = 
        custAccounts.FindAll(ac => ac.AccountHead.AccountHeadID 
                                   == accountHead.AccountHeadID );
   if(custAccountsFiltered != null)
      totalValue = custAccountsFiltered.Sum(account => 
                                            account.AccountValue).ToString();
}

注意:由于AccountValue不是null,Sum()函数不会返回null。,因此我可以直接使用它。


2

一周左右前,我们在开发团队中进行了讨论,并且几乎一致同意进行空回收。出于上述Mike的相同原因,一个人希望返回null。


2

空集合。如果您使用的是C#,则可以认为最大化系统资源不是必需的。虽然效率较低,但是返回Empty Collection对于所涉及的程序员来说更加方便(原因将在上面概述)。


2

在大多数情况下,返回空集合更好。

这样做的原因是实现调用方的便利性,一致的合同以及更容易实现。

如果方法返回null表示结果为空,则调用程序除枚举外还必须实现null检查适配器。然后,此代码在各个调用方中重复,因此为什么不将该适配器放在方法中以便可以重用。

IEnumerable的null的有效用法可能表示结果缺失或操作失败,但是在这种情况下,应考虑其他技术,例如引发异常。

using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;

namespace StackOverflow.EmptyCollectionUsageTests.Tests
{
    /// <summary>
    /// Demonstrates different approaches for empty collection results.
    /// </summary>
    class Container
    {
        /// <summary>
        /// Elements list.
        /// Not initialized to an empty collection here for the purpose of demonstration of usage along with <see cref="Populate"/> method.
        /// </summary>
        private List<Element> elements;

        /// <summary>
        /// Gets elements if any
        /// </summary>
        /// <returns>Returns elements or empty collection.</returns>
        public IEnumerable<Element> GetElements()
        {
            return elements ?? Enumerable.Empty<Element>();
        }

        /// <summary>
        /// Initializes the container with some results, if any.
        /// </summary>
        public void Populate()
        {
            elements = new List<Element>();
        }

        /// <summary>
        /// Gets elements. Throws <see cref="InvalidOperationException"/> if not populated.
        /// </summary>
        /// <returns>Returns <see cref="IEnumerable{T}"/> of <see cref="Element"/>.</returns>
        public IEnumerable<Element> GetElementsStrict()
        {
            if (elements == null)
            {
                throw new InvalidOperationException("You must call Populate before calling this method.");
            }

            return elements;
        }

        /// <summary>
        /// Gets elements, empty collection or nothing.
        /// </summary>
        /// <returns>Returns <see cref="IEnumerable{T}"/> of <see cref="Element"/>, with zero or more elements, or null in some cases.</returns>
        public IEnumerable<Element> GetElementsInconvenientCareless()
        {
            return elements;
        }

        /// <summary>
        /// Gets elements or nothing.
        /// </summary>
        /// <returns>Returns <see cref="IEnumerable{T}"/> of <see cref="Element"/>, with elements, or null in case of empty collection.</returns>
        /// <remarks>We are lucky that elements is a List, otherwise enumeration would be needed.</remarks>
        public IEnumerable<Element> GetElementsInconvenientCarefull()
        {
            if (elements == null || elements.Count == 0)
            {
                return null;
            }
            return elements;
        }
    }

    class Element
    {
    }

    /// <summary>
    /// http://stackoverflow.com/questions/1969993/is-it-better-to-return-null-or-empty-collection/
    /// </summary>
    class EmptyCollectionTests
    {
        private Container container;

        [SetUp]
        public void SetUp()
        {
            container = new Container();
        }

        /// <summary>
        /// Forgiving contract - caller does not have to implement null check in addition to enumeration.
        /// </summary>
        [Test]
        public void UseGetElements()
        {
            Assert.AreEqual(0, container.GetElements().Count());
        }

        /// <summary>
        /// Forget to <see cref="Container.Populate"/> and use strict method.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void WrongUseOfStrictContract()
        {
            container.GetElementsStrict().Count();
        }

        /// <summary>
        /// Call <see cref="Container.Populate"/> and use strict method.
        /// </summary>
        [Test]
        public void CorrectUsaOfStrictContract()
        {
            container.Populate();
            Assert.AreEqual(0, container.GetElementsStrict().Count());
        }

        /// <summary>
        /// Inconvenient contract - needs a local variable.
        /// </summary>
        [Test]
        public void CarefulUseOfCarelessMethod()
        {
            var elements = container.GetElementsInconvenientCareless();
            Assert.AreEqual(0, elements == null ? 0 : elements.Count());
        }

        /// <summary>
        /// Inconvenient contract - duplicate call in order to use in context of an single expression.
        /// </summary>
        [Test]
        public void LameCarefulUseOfCarelessMethod()
        {
            Assert.AreEqual(0, container.GetElementsInconvenientCareless() == null ? 0 : container.GetElementsInconvenientCareless().Count());
        }

        [Test]
        public void LuckyCarelessUseOfCarelessMethod()
        {
            // INIT
            var praySomeoneCalledPopulateBefore = (Action)(()=>container.Populate());
            praySomeoneCalledPopulateBefore();

            // ACT //ASSERT
            Assert.AreEqual(0, container.GetElementsInconvenientCareless().Count());
        }

        /// <summary>
        /// Excercise <see cref="ArgumentNullException"/> because of null passed to <see cref="Enumerable.Count{TSource}(System.Collections.Generic.IEnumerable{TSource})"/>
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void UnfortunateCarelessUseOfCarelessMethod()
        {
            Assert.AreEqual(0, container.GetElementsInconvenientCareless().Count());
        }

        /// <summary>
        /// Demonstrates the client code flow relying on returning null for empty collection.
        /// Exception is due to <see cref="Enumerable.First{TSource}(System.Collections.Generic.IEnumerable{TSource})"/> on an empty collection.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void UnfortunateEducatedUseOfCarelessMethod()
        {
            container.Populate();
            var elements = container.GetElementsInconvenientCareless();
            if (elements == null)
            {
                Assert.Inconclusive();
            }
            Assert.IsNotNull(elements.First());
        }

        /// <summary>
        /// Demonstrates the client code is bloated a bit, to compensate for implementation 'cleverness'.
        /// We can throw away the nullness result, because we don't know if the operation succeeded or not anyway.
        /// We are unfortunate to create a new instance of an empty collection.
        /// We might have already had one inside the implementation,
        /// but it have been discarded then in an effort to return null for empty collection.
        /// </summary>
        [Test]
        public void EducatedUseOfCarefullMethod()
        {
            Assert.AreEqual(0, (container.GetElementsInconvenientCarefull() ?? Enumerable.Empty<Element>()).Count());
        }
    }
}

2

我称之为十亿美元的错误……当时,我正在设计第一个全面的类型系统,以面向对象的语言进行引用。我的目标是确保对引用的所有使用都绝对安全,并由编译器自动执行检查。但是我无法抗拒引入空引用的诱惑,只是因为它是如此易于实现。这导致了无数错误,漏洞和系统崩溃,在最近四十年中可能造成十亿美元的痛苦和破坏。– ALGOL W的发明人Tony Hoare。

有关一般的详细说明,请参见此处null。我不同意undefined另一种说法null,但仍然值得一读。它解释了为什么您应该完全避免null,而不仅仅是在您提出要求的情况下。本质是,null在任何语言中这都是特例。您必须考虑null一个例外。undefined与此不同,在大多数情况下,处理未定义行为的代码只是一个错误。C语言和大多数其他语言也具有未定义的行为,但是大多数语言没有该语言的标识符。


1

从管理复杂性(软件工程的主要目标)的角度来看,我们希望避免向API的客户端传播不必要的循环复杂性。向客户返回null就像向客户返回另一个代码分支的循环复杂性成本。

(这对应于单元测试的负担。除了空集合的返回用例之外,您还需要针对空返回的用例编写测试。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.