ChatGPT解决这个技术问题 Extra ChatGPT

返回空集合还是空集合更好?

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

嗯,不完全是 CPerkins。 rads.stackoverflow.com/amzn/click/0321545613
@CPerkins - 是的,有。 Microsoft 自己的 .NET 框架设计指南中明确说明了这一点。有关详细信息,请参阅 RichardOD 的答案。
仅当含义是“我无法计算结果”时,您才应返回 null。 Null 永远不应该有“空”的语义,只有“缺失”或“未知”。我的文章中有关该主题的更多详细信息:blogs.msdn.com/ericlippert/archive/2009/05/14/…
Bozho:“任何”是非常多的语言。 Common Lisp 中的空列表完全等于空值呢? :-)
实际上,“重复”是关于返回对象而不是集合的方法。这是一个不同的场景,有不同的答案。

R
Ryan Lundy

空集合。总是。

这很糟糕:

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>() 可以被视为比返回(例如,新的空集合或数组)更有效。


我同意威尔的观点,但我认为“总是”有点过分。虽然空集合可能意味着“0 个项目”,但返回 Null 可能意味着“根本没有集合” - 例如。如果你正在解析 HTML,寻找一个 id="foo" 的
      可能会返回空集合;如果没有带有 id="foo" 的
        ,则返回 null 会更好(除非您想处理这种情况并出现异常)
    这并不总是“您是否可以轻松返回空数组”的问题,而是空数组在当前上下文中是否可能具有误导性的问题。一个空数组实际上意味着什么,null 也是如此。说你应该总是返回一个空数组而不是 null,几乎就像你说布尔方法应该总是返回 true 一样被误导。两个可能的值都传达了一个含义。
    您实际上应该更喜欢返回 System.Linq.Enumerable.Empty() 而不是新的 Foo[0]。它更明确,并为您节省了一次内存分配(至少,在我安装的 .NET 实现中)。
    @Will:OP:“您是否为具有集合作为返回类型的 method 返回 null 或空集合”。我认为在这种情况下,它是 IEnumerable 还是 ICollection 并不重要。无论如何,如果您选择 ICollection 类型的东西,它们也会返回 null...我希望它们返回一个空集合,但我遇到它们返回 null,所以我想我只是在这里提一下.我会说可枚举集合的默认值为空而不是空。我不知道这是一个如此敏感的话题。
    B
    Brian

    Framework Design Guidelines 2nd Edition(第 256 页):

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

    这是另一篇关于不返回空值的好处的有趣文章(我试图在 Brad Abram 的博客上找到一些东西,他链接到了这篇文章)。

    编辑- Eric Lippert 现在对原始问题发表了评论,我也想link to his excellent article


    +1。遵循框架设计指南始终是最佳实践,除非您有充分的理由不这样做。
    @Will,绝对。这就是我所遵循的。我从来没有发现任何需要做其他事情
    是的,这就是你所遵循的。但是,如果您依赖的 API 不遵循它,您就会被“困住”;)
    @Bozho-是的。您的回答为这些边缘案例提供了一些不错的答案。
    如果您对答案中最初提到的文章感兴趣并且不再可以访问,这里是(存档):Empty Arrays by Wesner Moise
    B
    Bozho

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

    null 可能意味着更具体的东西;

    您的 API(合同)可能会强制您返回 null。

    一些具体的例子:

    一个 UI 组件(来自您无法控制的库),如果传递一个空集合,则可能呈现一个空表,或者如果传递 null,则根本没有表。

    在 Object-to-XML (JSON/whatever) 中,null 表示元素丢失,而空集合将呈现冗余(并且可能不正确)

    您正在使用或实现一个明确声明应该返回/传递 null 的 API


    @Bozho- 这里有一些有趣的例子。
    我有点明白你的意思,尽管我认为你不应该围绕其他错误构建你的代码,并且根据 C# 中的定义“null”从不意味着特定的东西。值 null 被定义为“无信息”,因此说它携带特定信息是矛盾的。这就是为什么 .NET 指南规定如果集合确实为空,则应返回一个空集。返回 null 是说:“我不知道预期的集合去了哪里”
    不,这意味着“没有集合”而不是“集合没有元素”
    我曾经相信,然后我不得不写很多TSQL并了解到情况并非总是如此,呵呵。
    带有 Object-To-Xml 的部分是正确的
    J
    Jeff Sternal

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

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

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


    从技术上讲,我不认为这会返回一个空集合。
    @FryGuy - 不,不是。它返回一个 Enumerable 对象,其 GetEnumerator() 方法返回一个空的 Enumerator(通过与集合的类比)。也就是说,Enumerator 的 MoveNext() 方法总是返回 false。调用示例方法不会返回 null,也不会返回 GetEnumerator() 方法返回 null 的 Enumerable 对象。
    G
    George Polevoy

    Empty 对消费者更友好。

    有一个明确的方法可以组成一个空的可枚举:

    Enumerable.Empty<Element>()
    

    谢谢你的巧妙技巧。我像个白痴一样实例化了一个空的 List() ,但这看起来更干净,而且可能也更有效率。
    N
    Noctis

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

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

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

    回复桑德斯:

    是的,我假设“人没有回答问题”和“答案为零”之间存在差异。这就是我回答的最后一段的重点。许多程序无法将“不知道”与空白或零区分开来,这在我看来是一个潜在的严重缺陷。例如,大约一年前我正在买房子。我去了一个房地产网站,上面列出了许多要价为 0 美元的房子。对我来说听起来不错:他们免费赠送这些房子!但我确信可悲的现实是他们只是没有输入价格。在这种情况下,你可能会说,“嗯,显然为零意味着他们没有输入价格——没有人会免费赠送房子。”但该网站还列出了各个城镇房屋的平均要价和售价。我不禁想知道平均值是否不包括零,从而在某些地方给出了错误的低平均值。即 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 的函数就不足为奇了,我认为任何有能力的程序员都应该听说过,无论他是否认为这是一个好主意。我认为拥有一个单独的功能更像是一个“惊喜”问题。如果程序员不熟悉 API,当他在没有数据的情况下运行测试时,他会很快发现有时他会返回 null。但是他怎么会发现另一个函数的存在,除非他想到可能存在这样的函数并且他检查了文档,并且文档是完整且易于理解的?我宁愿有一个总是给我一个有意义的响应的函数,而不是我必须知道并记住调用两个函数的两个函数。


    为什么有必要将“无答案”和“零”响应组合在同一个返回值中?取而代之的是,让该方法返回“过去五年内的任何以前的住院情况”,并有一个单独的方法询问“以前的住院名单是否填写过?”。这是假设填写的未曾住院的名单与未填写的名单之间存在差异。
    但是,如果您返回 null,无论如何您已经给调用者带来了额外的负担!调用者必须检查每个返回值是否为空——这是一个可怕的 DRY 违规。如果你想单独指出“调用者没有回答问题”,创建一个额外的方法调用来表明事实会更简单。要么,要么使用具有 DeclinedToAnswer 额外属性的派生集合类型。永不返回 null 的规则根本不是任意的,它是最小意外原则。此外,你的方法的返回类型应该是它的名字所说的。 Null 几乎可以肯定不会。
    您假设您的 getHospitalizationList 仅从一个地方调用和/或所有调用者都希望区分“无应答”和“零”情况。在某些情况下(几乎可以肯定是大多数情况),您的调用者不需要进行这种区分,因此您强迫他们在不需要的地方添加空检查。这给你的代码库增加了很大的风险,因为人们很容易忘记这样做——而且因为很少有合法的理由返回 null 而不是集合,这更有可能发生。
    重新命名:没有函数名称可以完全描述函数的作用,除非它与函数一样长,因此非常不切实际。但无论如何,如果函数在没有给出答案的情况下返回一个空列表,那么按照同样的推理,它不应该被称为“getHospitalizationListOrEmptyListIfNoAnswer”吗?但实际上,您会坚持将 Java Reader.read 函数重命名为 readCharacterOrReturnMinusOneOnEndOfStream 吗?那 ResultSet.getInt 真的应该是“getIntOrZeroIfValueWasNull”吗?等等。
    RE 每个调用都想区分:嗯,是的,我假设,或者至少调用者的作者应该做出他不在乎的有意识的决定。如果函数返回一个“不知道”的空列表,而调用者盲目地对待这个“无”,它可能会给出严重不准确的结果。想象一下,如果函数是“getAllergicReactionToMedicationList”。盲目地将“未输入列表”视为“患者没有已知的过敏反应”的程序可能会导致患者死亡。在许多其他系统中,您会得到类似的结果,但结果可能不那么显着。 ...
    D
    David Hedlund

    如果一个空集合在语义上有意义,那是我更喜欢返回的。为 GetMessagesInMyInbox() 返回一个空集合表示“您的收件箱中确实没有任何消息”,而返回 null 可能有助于表示没有足够的数据来说明可能返回的列表应该是什么样子.


    在您给出的示例中,如果该方法无法满足请求而不是仅返回 null,则该方法可能应该引发异常。异常对于诊断问题比空值更有用。
    嗯,是的,在收件箱示例中,null 值肯定看起来不合理,我在考虑更笼统的术语。异常也可以很好地传达出现问题的事实,但是如果完全可以预期所指的“数据不足”,那么抛出异常将是糟糕的设计。我宁愿考虑一种情况,这种情况是完全有可能的,并且该方法有时无法计算响应完全没有错误。
    K
    Karmic Coder

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

    从语义上讲,null 和空列表的含义不同。差异是微妙的,在特定情况下,一种选择可能比另一种更好。

    无论您的选择如何,都要记录下来以避免混淆。


    在考虑 API 设计的正确性时,效率几乎不应该是一个因素。在一些非常具体的情况下,例如图形基元,可能是这样,但是在处理列表和大多数其他高级事物时,我非常怀疑它。
    同意 Greg 的观点,特别是考虑到 API 用户为补偿这种“优化”而编写的代码可能比一开始就使用更好的设计效率低下。
    同意,在大多数情况下,它根本不值得优化。使用现代内存管理,空列表实际上是免费的。
    D
    Dan

    有人可能会争辩说,Null Object Pattern 背后的原因类似于支持返回空集合的原因。


    L
    Larry Watanabe

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

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

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


    J
    Jason Baker

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

    如果您必须选择其中一个,我会说您应该倾向于一个空集合而不是空集合。但有时空集合与空值不同。


    m
    manuel aldana

    始终支持您的客户(使用您的 api):

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


    H
    Henric

    大约一周前,我们在工作的开发团队中进行了这样的讨论,我们几乎一致认为是空集合。一个人想返回 null 的原因与上面 Mike 指定的相同。


    m
    mothis

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


    N
    Noctis

    我想在这里用合适的例子来解释。

    在这里考虑一个案例..

    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。因此我可以直接使用它。


    G
    George Polevoy

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

    这样做的原因是调用者实现的便利、一致的合约和更容易的实现。

    如果方法返回 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());
            }
        }
    }
    

    c
    ceving

    我称其为我的十亿美元错误……当时,我正在为面向对象语言的引用设计第一个综合类型系统。我的目标是确保所有引用的使用都应该是绝对安全的,并由编译器自动执行检查。但我无法抗拒加入空引用的诱惑,仅仅是因为它很容易实现。这导致了无数的错误、漏洞和系统崩溃,在过去的四十年中可能造成了十亿美元的痛苦和损失。 ——托尼·霍尔,ALGOL W 的发明者。

    请参阅 here,了解关于 null 的详细说明。我不同意 undefined 是另一个 null 的说法,但它仍然值得一读。它解释了为什么你应该完全避免 null 而不仅仅是在你问的情况下。本质是,null 在任何语言中都是一种特殊情况。您必须将 null 视为一个例外。 undefined 在这方面有所不同,处理未定义行为的代码在大多数情况下只是一个错误。 C 和大多数其他语言也有未定义的行为,但它们中的大多数在语言中没有标识符。


    d
    dthal

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

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


    M
    Maria Ines Parnisari

    Go 似乎是一种语言,其中 nil 优于空数组。

    https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices

    声明空切片时,首选 var t []string 而非 t := []string{}。前者声明一个 nil 切片值,而后者是非 nil 但长度为零。它们在功能上是等效的——它们的 len 和 cap 都为零——但 nil 切片是首选样式。