简体   繁体   English

回null好还是空收好?

[英]Is it better to return null or empty collection?

That's kind of a general question (but I'm using C#), what's the best way (best practice), do you return null or empty collection for a method that has a collection as a return type?这是一个一般性问题(但我使用的是 C#),最好的方法是什么(最佳实践),对于以集合作为返回类型的方法,您是返回 null 还是返回空集合?

Empty collection.空集合。 Always.总是。

This sucks:这很糟糕:

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

It is considered a best practice to NEVER return null when returning a collection or enumerable.返回集合或可枚举时永远不要返回null被认为是最佳实践。 ALWAYS return an empty enumerable/collection.总是返回一个空的枚举/集合。 It prevents the aforementioned nonsense, and prevents your car getting egged by co-workers and users of your classes.它可以防止上述废话,并防止您的汽车被同事和班级用户怂恿。

When talking about properties, always set your property once and forget it在谈论属性时,总是设置你的属性一次然后忘记它

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

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

In .NET 4.6.1, you can condense this quite a lot:在 .NET 4.6.1 中,你可以浓缩很多:

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

When talking about methods that return enumerables, you can easily return an empty enumerable instead of null ...在谈论返回枚举的方法时,您可以轻松地返回一个空的枚举而不是null ...

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

Using Enumerable.Empty<T>() can be seen as more efficient than returning, for example, a new empty collection or array.使用Enumerable.Empty<T>()可以被视为比返回更有效,例如,一个新的空集合或数组。

From the Framework Design Guidelines 2nd Edition (pg. 256):来自框架设计指南第 2 版(第 256 页):

DO NOT return null values from collection properties or from methods returning collections.不要从集合属性或从返回集合的方法中返回空值。 Return an empty collection or an empty array instead.而是返回一个空集合或一个空数组。

Here's another interesting article on the benefits of not returning nulls (I was trying to find something on Brad Abram's blog, and he linked to the article).这是另一篇关于不返回空值的好处的有趣文章(我试图在 Brad Abram 的博客上找到一些东西,他链接到了这篇文章)。

Edit- as Eric Lippert has now commented to the original question, I'd also like to link to his excellent article .编辑 -正如埃里克·利珀特 (Eric Lippert) 现在对原始问题的评论,我还想链接到他的优秀文章

Depends on your contract and your concrete case .取决于你的合同和你的具体情况 Generally it's best to return empty collections , but sometimes ( rarely ):通常最好返回空集合,但有时(很少):

  • null might mean something more specific; null可能意味着更具体的东西;
  • your API (contract) might force you to return null .您的 API(合同)可能会强制您返回null

Some concrete examples:一些具体的例子:

  • an UI component (from a library out of your control), might be rendering an empty table if an empty collection is passed, or no table at all, if null is passed. UI 组件(来自不受您控制的库)如果传递空集合,则可能会呈现空表,或者如果传递 null 则根本没有表。
  • in a Object-to-XML (JSON/whatever), where null would mean the element is missing, while an empty collection would render a redundant (and possibly incorrect) <collection />在 Object-to-XML (JSON/whatever) 中, null表示元素丢失,而空集合将呈现冗余(并且可能不正确) <collection />
  • you are using or implementing an API which explicitly states that null should be returned/passed您正在使用或实现一个 API,该 API 明确指出应返回/传递 null

There is one other point that hasn't yet been mentioned.还有一点还没有提到。 Consider the following code:考虑以下代码:

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

The C# Language will return an empty enumerator when calling this method.调用此方法时,C# 语言将返回一个空的枚举数。 Therefore, to be consistant with the language design (and, thus, programmer expectations) an empty collection should be returned.因此,为了与语言设计(以及程序员的期望)保持一致,应该返回一个空集合。

Empty is much more consumer friendly. Empty 对消费者更加友好。

There is a clear method of making up an empty enumerable:有一个明确的方法可以组成一个空的可枚举:

Enumerable.Empty<Element>()

It seems to me that you should return the value that is semantically correct in context, whatever that may be.在我看来,您应该返回在上下文中语义正确的值,无论它是什么。 A rule that says "always return an empty collection" seems a little simplistic to me.一条说“总是返回一个空集合”的规则对我来说似乎有点简单。

Suppose in, say, a system for a hospital, we have a function that is supposed to return a list of all previous hospitalizations for the past 5 years.假设在一个医院系统中,我们有一个函数应该返回过去 5 年所有先前住院的列表。 If the customer has not been in the hospital, it makes good sense to return an empty list.如果客户没有在医院,返回一个空列表是很有意义的。 But what if the customer left that part of the admittance form blank?但是,如果客户将准入表的那部分留空怎么办? We need a different value to distinguish "empty list" from "no answer" or "don't know".我们需要一个不同的值来区分“空列表”和“没有答案”或“不知道”。 We could throw an exception, but it's not necessarily an error condition, and it doesn't necessarily drive us out of the normal program flow.我们可以抛出异常,但它不一定是错误条件,也不一定会使我们脱离正常的程序流程。

I've often been frustrated by systems that cannot distinguish between zero and no answer.我经常对无法区分零答案和无答案的系统感到沮丧。 I've had a number of times where a system has asked me to enter some number, I enter zero, and I get an error message telling me that I must enter a value in this field.我曾多次遇到系统要求我输入某个数字,我输入零,然后收到一条错误消息,告诉我必须在该字段中输入一个值。 I just did: I entered zero!我刚刚做到了:我输入了零! But it won't accept zero because it can't distinguish it from no answer.但它不会接受零,因为它无法将它与没有答案区分开来。


Reply to Saunders:回复桑德斯:

Yes, I'm assuming that there's a difference between "Person didn't answer the question" and "The answer was zero."是的,我假设“人员没有回答问题”和“答案为零”之间存在差异。 That was the point of the last paragraph of my answer.这是我回答最后一段的重点。 Many programs are unable to distinguish "don't know" from blank or zero, which seems to me a potentially serious flaw.许多程序无法区分“不知道”与空白或零,这在我看来是一个潜在的严重缺陷。 For example, I was shopping for a house a year or so ago.例如,大约一年前我正在买房子。 I went to a real estate web site and there were many houses listed with an asking price of $0.我去了一个房地产网站,上面有很多房子的要价是 0 美元。 Sounded pretty good to me: They're giving these houses away for free!对我来说听起来不错:他们免费赠送这些房子! But I'm sure the sad reality was that they just hadn't entered the price.但我确信可悲的现实是他们只是没有输入价格。 In that case you may say, "Well, OBVIOUSLY zero means they didn't enter the price -- nobody's going to give a house away for free."在这种情况下,你可能会说,“嗯,显然零意味着他们没有输入价格——没有人会免费赠送房子。” But the site also listed the average asking and selling prices of houses in various towns.但该网站还列出了各个城镇房屋的平均要价和售价。 I can't help but wonder if the average didn't include the zeros, thus giving an incorrectly low average for some places.我不禁想知道平均值是否不包括零,从而在某些地方给出了错误的低平均值。 ie what is the average of $100,000;即 100,000 美元的平均值是多少; $120,000; 120,000 美元; and "don't know"?和“不知道”? Technically the answer is "don't know".从技术上讲,答案是“不知道”。 What we probably really want to see is $110,000.我们可能真正想看到的是 110,000 美元。 But what we'll probably get is $73,333, which would be completely wrong.但我们可能会得到 73,333 美元,这是完全错误的。 Also, what if we had this problem on a site where users can order on-line?另外,如果我们在用户可以在线订购的网站上遇到此问题怎么办? (Unlikely for real estate, but I'm sure you've seen it done for many other products.) Would we really want "price not specified yet" to be interpreted as "free"? (不太可能用于房地产,但我相信您已经看到它用于许多其他产品。)我们真的希望将“价格尚未指定”解释为“免费”吗?

RE having two separate functions, an "is there any?" RE 有两个独立的功能,一个“有吗?” and an "if so, what is it?"和“如果是这样,那是什么?” Yes, you certainly could do that, but why would you want to?是的,您当然可以这样做,但是您为什么要这样做? Now the calling program has to make two calls instead of one.现在调用程序必须进行两次调用而不是一次调用。 What happens if a programmer fails to call the "any?"如果程序员没有调用“any”会发生什么? and goes straight to the "what is it?"并直接进入“这是什么?” ? ? Will the program return a mis-leading zero?程序会返回一个错误的前导零吗? Throw an exception?抛出异常? Return an undefined value?返回一个未定义的值? It creates more code, more work, and more potential errors.它会创建更多代码、更多工作和更多潜在错误。

The only benefit I see is that it enables you to comply with an arbitrary rule.我看到的唯一好处是它使您能够遵守任意规则。 Is there any advantage to this rule that makes it worth the trouble of obeying it?这条规则有什么好处值得去遵守它吗? If not, why bother?如果没有,为什么要打扰?


Reply to Jammycakes:回复果酱蛋糕:

Consider what the actual code would look like.考虑实际代码的样子。 I know the question said C# but excuse me if I write Java.我知道这个问题说的是 C#,但是如果我写 Java,请原谅我。 My C# isn't very sharp and the principle is the same.我的C#不是很犀利,原理是一样的。

With a null return:返回空值:

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

With a separate function:具有单独的功能:

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

It's actually a line or two less code with the null return, so it's not more burden on the caller, it's less.它实际上是少了一两行返回空值的代码,所以它不是调用者的更多负担,而是更少。

I don't see how it creates a DRY issue.我看不出它是如何产生 DRY 问题的。 It's not like we have to execute the call twice.这不像我们必须执行两次调用。 If we always wanted to do the same thing when the list does not exist, maybe we could push handling down to the get-list function rather than having the caller do it, and so putting the code in the caller would be a DRY violation.如果我们总是想在列表不存在时做同样的事情,也许我们可以将处理下推到 get-list 函数而不是让调用者来做,因此将代码放在调用者中将是 DRY 违规。 But we almost surely don't want to always do the same thing.但我们几乎肯定不想总是做同样的事情。 In functions where we must have the list to process, a missing list is an error that might well halt processing.在我们必须处理列表的函数中,缺少列表是一个错误,很可能会停止处理。 But on an edit screen, we surely don't want to halt processing if they haven't entered data yet: we want to let them enter data.但是在编辑屏幕上,如果他们还没有输入数据,我们肯定不想停止处理:我们想让他们输入数据。 So handling "no list" must be done at the caller level one way or another.因此,必须以一种或另一种方式在调用者级别处理“无列表”。 And whether we do that with a null return or a separate function makes no difference to the bigger principle.无论我们是使用 null 返回还是单独的函数来实现这一点,对于更大的原则都没有区别。

Sure, if the caller doesn't check for null, the program could fail with a null-pointer exception.当然,如果调用者不检查 null,程序可能会因空指针异常而失败。 But if there's a separate "got any" function and the caller doesn't call that function but blindly calls the "get list" function, then what happens?但是如果有一个单独的“get any”函数并且调用者没有调用该函数而是盲目地调用“get list”函数,那么会发生什么? If it throws an exception or otherwise fails, well, that's pretty much the same as what would happen if it returned null and didn't check for it.如果它抛出异常或以其他方式失败,那么这与如果它返回 null 并且没有检查它会发生的情况几乎相同。 If it returns an empty list, that's just wrong.如果它返回一个空列表,那是错误的。 You're failing to distinguish between "I have a list with zero elements" and "I don't have a list".您无法区分“我有一个包含零个元素的列表”和“我没有一个列表”。 It's like returning zero for the price when the user didn't enter any price: it's just wrong.这就像当用户没有输入任何价格时为价格返回零:这是错误的。

I don't see how attaching an additional attribute to the collection helps.我看不出将附加属性附加到集合中有何帮助。 The caller still has to check it.调用者仍然必须检查它。 How is that better than checking for null?这比检查 null 有什么好处? Again, the absolute worst thing that could happen is for the programmer to forget to check it, and give incorrect results.同样,可能发生的最糟糕的事情是程序员忘记检查它,并给出错误的结果。

A function that returns null is not a surprise if the programmer is familiar with the concept of null meaning "don't have a value", which I think any competent programmer should have heard of, whether he thinks it's a good idea or not.如果程序员熟悉 null 的概念,意思是“没有值”,那么返回 null 的函数并不奇怪,我认为任何有能力的程序员都应该听说过,无论他是否认为这是一个好主意。 I think having a separate function is more of a "surprise" problem.我认为拥有一个单独的功能更像是一个“惊喜”问题。 If a programmer is unfamiliar with the API, when he runs a test with no data he'll quickly discover that sometimes he gets back a null.如果程序员不熟悉 API,当他在没有数据的情况下运行测试时,他会很快发现有时他会返回空值。 But how would he discover the existence of another function unless it occurred to him that there might be such a function and he checks the documentation, and the documentation is complete and comprehensible?但是他怎么会发现另一个功能的存在,除非他想到可能有这样一个功能,他检查了文档,并且文档是完整和可理解的? I would much rather have one function that always gives me a meaningful response, rather than two functions that I have to know and remember to call both.我宁愿拥有一个总是能给我有意义的响应的函数,而不是我必须知道并记住调用这两个函数的两个函数。

If an empty collection makes sense semantically, that's what I prefer to return.如果一个空集合在语义上有意义,那就是我更喜欢返回的。 Returning an empty collection for GetMessagesInMyInbox() communicates "you really do not have any messages in your inbox", whereas returning null might be useful to communicate that insufficient data is available to say what the list that might be returned ought to look like.GetMessagesInMyInbox()返回一个空集合表示“您的收件箱中确实没有任何消息”,而返回null可能有助于传达没有足够的数据来说明可能返回的列表应该是什么样子。

Returning null could be more efficient, as no new object is created.返回 null 可能更有效,因为没有创建新对象。 However, it would also often require a null check (or exception handling.)但是,它通常还需要null检查(或异常处理)。

Semantically, null and an empty list do not mean the same thing.从语义上讲, null和空列表的含义不同。 The differences are subtle and one choice may be better than the other in specific instances.差异是微妙的,在特定情况下,一种选择可能比另一种更好。

Regardless of your choice, document it to avoid confusion.无论您的选择如何,请记录下来以避免混淆。

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

Depends on the situation.视情况而定。 If it is a special case, then return null.如果是特殊情况,则返回null。 If the function just happens to return an empty collection, then obviously returning that is ok.如果函数恰好返回一个空集合,那么显然返回是可以的。 However, returning an empty collection as a special case because of invalid parameters or other reasons is NOT a good idea, because it is masking a special case condition.但是,由于参数无效或其他原因将空集合作为特殊情况返回并不是一个好主意,因为它掩盖了特殊情况。

Actually, in this case I usually prefer to throw an exception to make sure it is REALLY not ignored :)实际上,在这种情况下,我通常更喜欢抛出异常以确保它真的不会被忽略:)

Saying that it makes the code more robust (by returning an empty collection) as they do not have to handle the null condition is bad, as it is simply masking a problem that should be handled by the calling code.说它使代码更健壮(通过返回一个空集合),因为它们不必处理 null 条件是不好的,因为它只是掩盖了应该由调用代码处理的问题。

I would argue that null isn't the same thing as an empty collection and you should choose which one best represents what you're returning.我认为null与空集合不同,您应该选择哪一个最能代表您要返回的内容。 In most cases null is nothing (except in SQL).在大多数情况下null什么都不是(除了在 SQL 中)。 An empty collection is something, albeit an empty something.一个空的集合是一些东西,尽管是一个空的东西。

If you have have to choose one or the other, I would say that you should tend towards an empty collection rather than null.如果您必须选择其中之一,我会说您应该倾向于空集合而不是空集合。 But there are times when an empty collection isn't the same thing as a null value.但有时空集合与空值不同。

Think always in favor of your clients (which are using your api):始终为您的客户(正在使用您的 api)考虑:

Returning 'null' very often makes problems with clients not handling null checks correctly, which causes a NullPointerException during runtime.返回 '​​null' 通常会导致客户端无法正确处理 null 检查,从而导致运行时出现 NullPointerException。 I have seen cases where such a missing null-check forced a priority production issue (a client used foreach(...) on a null value).我见过这样一个缺失的空检查迫使优先生产问题的情况(客户端在空值上使用 foreach(...) )。 During testing the problem did not occur, because the data operated on was slightly different.在测试过程中没有出现问题,因为操作的数据略有不同。

I like to give explain here, with suitable example.我喜欢在这里给出解释,并给出合适的例子。

Consider a case here..考虑一个案例在这里..

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

Here Consider the functions I am using ..这里考虑我正在使用的功能..

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

I can easily use ListCustomerAccount and FindAll instead of.,我可以轻松地使用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();
}

NOTE : Since AccountValue is not null , the Sum() function will not return null ., Hence I can use it directly.注意:由于 AccountValue 不是null ,Sum() 函数不会返回null 。因此我可以直接使用它。

We had this discussion among the development team at work a week or so ago, and we almost unanimously went for empty collection.大约一周前,我们在工作中的开发团队中进行了此讨论,我们几乎一致同意进行空收集。 One person wanted to return null for the same reason Mike specified above.出于 Mike 上面指定的相同原因,有人想要返回 null。

Empty Collection.空集合。 If you're using C#, the assumption is that maximizing system resources is not essential.如果您使用 C#,则假设最大化系统资源不是必需的。 While less efficient, returning Empty Collection is much more convenient for the programmers involved (for the reason Will outlined above).虽然效率较低,但返回 Empty Collection 对所涉及的程序员来说要方便得多(原因 Will 已在上面概述)。

Returning an empty collection is better in most cases.在大多数情况下,返回空集合更好。

The reason for that is convenience of implementation of the caller, consistent contract, and easier implementation.这样做的原因是调用方的实现方便,契约一致,更容易实现。

If a method returns null to indicate empty result, the caller must implement a null checking adapter in addition to enumeration.如果方法返回 null 表示结果为空,则调用者除了枚举之外还必须实现一个空检查适配器。 This code is then duplicated in various callers, so why not to put this adapter inside the method so it could be reused.此代码随后在各种调用方中复制,所以为什么不将此适配器放在方法中以便可以重用。

A valid usage of null for IEnumerable might be an indication of absent result, or an operation failure, but in this case other techniques should be considered, such as throwing an exception.对 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());
        }
    }
}

I call it my billion-dollar mistake…At that time, I was designing the first comprehensive type system for references in an object-oriented language.我称其为我的十亿美元错误……当时,我正在为面向对象语言中的引用设计第一个综合类型系统。 My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler.我的目标是确保所有引用的使用都绝对安全,并由编译器自动执行检查。 But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement.但是我无法抗拒放入空引用的诱惑,仅仅因为它很容易实现。 This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.这导致了无数错误、漏洞和系统崩溃,在过去的四十年里,这些问题可能造成了 10 亿美元的痛苦和损失。 Tony Hoare, inventor of ALGOL W. ——托尼·霍尔,ALGOL W 的发明者。

See here for an elaborate shit storm about null in general. 在这里看到一个关于null的精心设计的狗屎风暴。 I do not agree with the statement that undefined is another null , but it is still worth reading.我不同意undefined是另一个null的说法,但它仍然值得一读。 And it explains, why you should avoid null at all and not just in the case you have asked.它解释了为什么你应该完全避免null而不仅仅是在你提出的情况下。 The essence is, that null is in any language a special case.本质是, null在任何语言中都是一个特例。 You have to think about null as an exception.您必须将null视为例外。 undefined is different in that way, that code dealing with undefined behavior is in most cases just a bug. undefined在这方面有所不同,处理未定义行为的代码在大多数情况下只是一个错误。 C and most other languages have also undefined behavior but most of them have no identifier for that in the language. C 和大多数其他语言也有未定义的行为,但其中大多数在语言中没有标识符。

From the perspective of managing complexity, a primary software engineering objective, we want to avoid propagating unnecessary cyclomatic complexity to the clients of an API.从管理复杂性的角度来看,这是一个主要的软件工程目标,我们希望避免将不必要的圈复杂性传播给 API 的客户端。 Returning a null to the client is like returning them the cyclomatic complexity cost of another code branch.向客户端返回空值就像向他们返回另一个代码分支的圈复杂度成本。

(This corresponds to a unit testing burden. You would need to write a test for the null return case, in addition to the empty collection return case.) (这对应于单元测试负担。除了空集合返回情况之外,您还需要为空返回情况编写测试。)

Go seems to be the one language where nil is preferred over an empty array. Go 似乎是nil优于空数组的一种语言。

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

When declaring an empty slice, prefer var t []string over t:= []string{} .声明空切片时,首选var t []string而不是t:= []string{} The former declares a nil slice value, while the latter is non- nil but zero-length.前者声明一个nil切片值,而后者是非nil但长度为零。 They are functionally equivalent—their len and cap are both zero—but the nil slice is the preferred style.它们在功能上是等价的——它们的lencap都是零——但nil切片是首选样式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM