ChatGPT解决这个技术问题 Extra ChatGPT

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 ?

@CPerkins - Yes, there is. It is clearly stated in Microsoft's own .NET framework design guidelines. See RichardOD's answer for the details.
ONLY if the meaning is "I cannot compute the results" should you return null. Null should never have the semantics of "empty", only "missing" or "unknown". More details in my article on the subject: blogs.msdn.com/ericlippert/archive/2009/05/14/…
Bozho: "any" is an awful lot of languages. What about Common Lisp, where the empty list is exactly equal to the null value? :-)
Actually, the "duplicate" is about methods which return an object, not a collection. It's a different scenario with different answers.

R
Ryan Lundy

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. 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:

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...

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.


I agree with Will, but i think that "always" is a little excessive. While an empty collection might mean "0 items", returning Null could mean "no collection at all" - eg. if you are parsing HTML, looking for an
    with id="foo",
      could return empty collection; if there is no
        with id="foo" a null return would be better (unless you want to handle this case with an exception)
    it's not always a question of whether or not "you can easily return an empty array", but rather of whether or not an empty array might be misleading in the current context. An empty array actually means something, as does null. To say that you should always return an empty array rather than null, is almost as misguided as saying you a boolean method should always return true. Both possible values convey a meaning.
    You should actually prefer returning System.Linq.Enumerable.Empty() instead of a new Foo[0]. It is more explicit and saves you one memory allocation (at least, in my installed .NET implementation).
    @Will: OP:"do you return null or empty collection for a method that has a collection as a return type". I think in this case whether it's IEnumerable or ICollection doesn't matter that much. Anyway, if you select something of type ICollection they also return null... I would like them to return an empty collection, but I ran into them returning null, so I thought I'd just mention it here. I would say the default of a collection of enumerable is empty not null. I didn't know it was such a sensitive subject.
    B
    Brian

    From the Framework Design Guidelines 2nd Edition (pg. 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).

    Edit- as Eric Lippert has now commented to the original question, I'd also like to link to his excellent article.


    +1. It is always best practice to follow the framework design guidelines unless you have a very VERY good reason not to.
    @Will, absolutely. That's what I follow. I've never found any need to do otherwise
    yup, that's what you follow. But in cases that APIs you rely on don't follow it, you are 'trapped' ;)
    @Bozho- yeap. Your answer provides some nice answers of those fringe cases.
    If you are interested in the article that was originally mentioned in the answer and that is no longer accessible, here it is (archived): Empty Arrays by Wesner Moise.
    B
    Bozho

    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;

    your API (contract) might force you to return 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.

    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)

    you are using or implementing an API which explicitly states that null should be returned/passed


    @Bozho- some interesting examples here.
    I'd kinda see your point eventhough I don't think you should build you code around other fault and by definition 'null' in c# never means something specific. The value null is defined as "no information" and hence to say that it carries specific information is an oximoron. That's why the .NET guidelines state that you should return an empty set if the set is indeed empty. returning null is saying: "I don't know where the expected set went"
    no, it means "there is no set" rather than "the set has no elements"
    I used to believe that, then I had to write lots of TSQL and learned that's not always the case, heheh.
    The part with Object-To-Xml is spot on
    J
    Jeff Sternal

    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. Therefore, to be consistant with the language design (and, thus, programmer expectations) an empty collection should be returned.


    I don't technically think that this returns an empty collection.
    @FryGuy - No it doesn't. It returns an Enumerable object, whose GetEnumerator() method returns an Enumerator that is (by analagy with a collection) empty. That is, the Enumerator's MoveNext() method always returns false. Calling the example method does not return null, nor does it return an Enumerable object whose GetEnumerator() method returns null.
    G
    George Polevoy

    Empty is much more consumer friendly.

    There is a clear method of making up an empty enumerable:

    Enumerable.Empty<Element>()
    

    Thanks for the neat trick. I was instantiating an empty List() like an idiot but this looks a lot cleaner and is probably a little more efficient too.
    N
    Noctis

    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. 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. 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. i.e. what is the average of $100,000; $120,000; and "don't know"? Technically the answer is "don't know". What we probably really want to see is $110,000. But what we'll probably get is $73,333, which would be completely wrong. 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?" 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?" 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. My C# isn't very sharp and the principle is the same.

    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. 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. 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.

    Sure, if the caller doesn't check for null, the program could fail with a null-pointer exception. 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? 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. 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? 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. 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. 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.


    Why is it necessary to combine the "no answer" and "zero" responses in the same return value? Instead, have the method return "any previous hospitalizations in the past five years", and have a separate method that asks, "was the previous hospitalizations list ever filled in?". That assumes there's a difference between a list filled in with no previous hospitalizations, and a list not filled in.
    But if you return null you are already placing an extra burden on on the caller anyway! The caller has to check every return value for null -- a horrible DRY violation. If you want to indicate "caller did not answer the question" separately, it is simpler to create an extra method call to indicate the fact. Either that, or use a derived collection type with an extra property of DeclinedToAnswer. The rule to never return null is not arbitrary at all, it is the Principle of Least Surprise. Also, your method's return type should mean what its name says it does. Null almost certainly doesn't.
    You're assuming that your getHospitalizationList is only called from one place and/or that all its callers will want to distinguish between the "no answer" and "zero" cases. There will be cases (almost certainly a majority) where your caller does not need to make that distinction, and so you are forcing them to add null checks in places where this should not be necessary. This adds significant risk to your codebase because people can all too easily forget to do so -- and because there are very few legitimate reasons to return null instead of a collection, this will be much more likely.
    RE the name: No function name can completely describe what the function does unless it is as long as the function, and thus wildly impractical. But in any case, if the function returns an empty list when no answer was given, then by the same reasoning, shouldn't it be called "getHospitalizationListOrEmptyListIfNoAnswer"? But really, would you insist that the Java Reader.read function should be renamed readCharacterOrReturnMinusOneOnEndOfStream? That ResultSet.getInt should really be "getIntOrZeroIfValueWasNull"? Etc.
    RE every call wants to distinguish: Well, yes, I'm assuming that, or at least that the author of a caller should make a conscious decision that he does not care. If the function returns an empty list for "don't know", and callers blindly treat this "none", it could give seriously inaccurate results. Imagine if the function was "getAllergicReactionToMedicationList". A program that blindly treated "list was not entered" as "patient has no known allergic reactions" could literally result in killing a paitent. You'd get similar, if less dramatic results, in many other systems. ...
    D
    David Hedlund

    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.


    In the example you give, it sounds like the method should probably throw an exception if it can't fulfil the request rather than just returning null. Exceptions are much more useful for diagnosing problems than nulls.
    well yes, in the inbox example a null value surely doesn't appear reasonable, i was thinking in more general terms on that one. Exceptions are also great to communicate the fact that something has gone wrong, but if the "insufficient data" referred to is perfectly expected, then throwing an exception there would be poor design. I'm rather thinking of a scenario where it is perfectly possible and no error at all for the method to sometimes not be able to calculate a response.
    K
    Karmic Coder

    Returning null could be more efficient, as no new object is created. However, it would also often require a null check (or exception handling.)

    Semantically, null and an empty list do not mean the same thing. 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.


    Efficiency should almost never be a factor when considering the correctness of the design of an API. In some very specific cases such as graphics primitives then it may be so, but when dealing with lists and most other high-level things then I very much doubt it.
    Agree with Greg, especially given that the code the API user has to write to compensate for this "optimization" may be more inefficient than if a better design were used in the first place.
    Agreed, and in most cases it simply isn't worth the optimization. Empty lists are practically free with modern memory management.
    D
    Dan

    One could argue that the reasoning behind Null Object Pattern is similar to one in favour of returning the empty collection.


    L
    Larry Watanabe

    Depends on the situation. If it is a special case, then return 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.


    J
    Jason Baker

    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. In most cases null is nothing (except in 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.


    m
    manuel aldana

    Think always in favor of your clients (which are using your api):

    Returning 'null' very often makes problems with clients not handling null checks correctly, which causes a NullPointerException during runtime. I have seen cases where such a missing null-check forced a priority production issue (a client used foreach(...) on a null value). During testing the problem did not occur, because the data operated on was slightly different.


    H
    Henric

    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.


    m
    mothis

    Empty Collection. If you're using C#, the assumption is that maximizing system resources is not essential. While less efficient, returning Empty Collection is much more convenient for the programmers involved (for the reason Will outlined above).


    N
    Noctis

    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.,

    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.


    G
    George Polevoy

    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. 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.

    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

    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. – Tony Hoare, inventor of ALGOL W.

    See here for an elaborate shit storm about null in general. I do not agree with the statement that undefined is another null, but it is still worth reading. And it explains, why you should avoid null at all and not just in the case you have asked. The essence is, that null is in any language a special case. You have to think about null as an exception. undefined is different in that way, that code dealing with undefined behavior is in most cases just a bug. C and most other languages have also undefined behavior but most of them have no identifier for that in the language.


    d
    dthal

    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. 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.)


    M
    Maria Ines Parnisari

    Go seems to be the one language where nil is preferred over an empty array.

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

    When declaring an empty slice, prefer var t []string over t := []string{}. The former declares a nil slice value, while the latter is non-nil but zero-length. They are functionally equivalent—their len and cap are both zero—but the nil slice is the preferred style.