|
A reference type either can point to a object living in the Heap or be null, in garbage collected languages like Java and C#.
Null does not mean a "non existent" list. Its meaning is closer to the machine than the problem domain. A field/variable/parameter with "null" pretends to be a proper object ready to be useful, but when you try to do something with it, a disgusting NullPointException pops up in our faces. This has no counterpart in real life, null is a invalid memory address pointing to no object at all.
It is so easy to forget checking all null references, and they spread in our code, screwing all our program. We need to debug the hell out of our program and backtrack to the exact point we let a null be passed instead a proper object.
An Optional<thing> is the best option to represent something which can be potentially be missing. By using optional, the compiler gets in our way and warn us that the thing should be tested for nullity. By using null references, the compiler is completely blind to the fact the reference may not be a proper object, and it is very easy to forget to test if the object is missing.
|
|
|
|
|
Many of the arguments for null lists posited here are grounded in illogical assumptions. For example, we see Alice giving Bob an empty list, which prompts him to go to the store. Oh, if only the list had been null instead! No, wait. Alice is mean-hearted, making Bob go to the store for no reason. In the real world, Alice should have checked the list first, make sure it wasn't empty, and then tell Bob to go to the store. It's not Bob's responsibility to check if the list is empty, it's Alice's. Bob will be lot more efficient when Alice isn't bothering him all the time with requests that are meaningless.
The stamp-collecting example is also pretty silly. Instead, if you wanted to know if there were stamp collectors, you'd use an Interface (say, IStampCollector with a method getStamps). This way, Alice doesn't have any stamp data associated with her, because she doesn't support stamp collecting. You're right about the fact that Alice shouldn't return a list of stamps, but she also shouldn't support a request to get her list of stamps, either. You should simply be able so say something like the following:
if(alice instanceOf IStampCollector) {
Stamps[] stamps = (IStampCollector))alice).getStamps();
}
There's no distinctive difference between saying "this list is null" and "this list is empty", except that they are semantically different. A null typically means "invalid" or "uninitialized" data. Ideally, we should only use null to indicate unknown values. Semantically, conflating null with empty lists means that we don't know if a call was successful, but returned no data, or there was an error, perhaps because the data was unavailable or unknown. A null list brings to mind something that should be an exception.
While there are perfectly appropriate places for null lists, such as indicating a list that has no yet been loaded (for the lazy-loading pattern), lists should generally not be null in practice. This causes a list of zero values to be treated differently than a list of one or more values, which generally only matters in certain situations, such as calculating the average of a list of values (since you can't divide by zero), or when you need to output "no results", in which case, you may as well check for size() == 0. Having to check for both null and size() == 0 repeatedly just bloats code and makes it harder to understand and trust. Return values should avoid returning null lists when possible, preferring either a non-null list, or an exception if an error occurred.
In terms of memory and performance, some languages do, unfortunately, have an advantage when using null over returning an empty collection. However, this is language dependent and should not be taken as gospel across all languages or platforms. In general terms, an empty list uses 4 bytes of memory on a 32-bit platform, and 8 bytes on a 64-bit platform. The null value itself uses the same amount of space, it just points to a deduplicated area of memory. Caching a single empty collection negates the advantage of using null for memory. As for processor time used, the typical application won't see any significant performance boost from using null versus an empty list; they are used far less frequently than the bulk of the algorithm's CPU time.
Finally, there's not a binary group of developers, the avoid-nulls and love-nulls. There's a third group that's in between, the avoid-bugs group: they use null values to represent invalid or unknown data, and non-null values the rest of the time. They don't use null in places it doesn't make sense, nor do they restructure their code to avoid nulls by using placeholders. They simply accept that nulls have a natural utility within the programming language, and should be used appropriately. Null lists are hardly ever appropriate, but the avoid-bugs group will certainly use them when called for.
modified 9-Feb-17 11:15am.
|
|
|
|
|
Tony Hoare, the inventor of the null reference (he created it in one of the first OOP languages, ALGOL W) calls it his "billion dollar mistake". He fully admits it was a really stupid idea, and that null is not equivalent to empty or no data. Unfortunately, we're stuck with it.. and we have to use it in many situations (typically with frameworks we don't control). But, that doesn't mean we have to keep perpetuating this problem ourselves. null is, at it's core, an abomination. More modern languages, like C# or Java (along with modern OS's with MMU's) have at least made it possible to trap this error (it used to be that the computer would blindly use 0 as a reference point and write to memory offsets from it). But that doesn't make it any less of pariah in my view. I'm not giving you a vote of 1, because I agree with you that empty lists are not always the choice, but null is never the correct choice.
By the way, your arguments above are flawed. In the case of Alice's shopping list. Bob would not go to the grocery store then look at the list, go through the checkout line and get an empty box. Bob would note the list is empty and never leave for the grocery store in the first place.
In the same way, if you get an empty list, your foreach would simply do nothing and perform no work (other than to look at the list and decide there is nothing to iterate over).
The flaw here is that software often has strict requirements about return types. And that can mean you have to abstract your return types. So, your return type may not be an empty list, but something that represents Nothing (but is not Null). In C# they have the concept of Nullable<t>, but this is reserved for value types and is a structure (aka a value type) that represents null (but is not actually null). The same can be done with an object that represent Nothing. You can have Nothing<t> object for types that can return Nothing.
modified 26-Jan-15 12:16pm.
|
|
|
|
|
Erik Funkenbusch wrote: Tony Hoare, the inventor of the null reference ... fully admits it was a really stupid idea
I think there is (understandably) a quite common misconception of "Tony Hoare admits the invention of null was a really stupid idea".
Tony Hoare doesn't think that the introduction of null per se is a mistake, but the problem is the lack of support for null handling in programming languages. He puts it like this in his famous speech:
"More recent programming languages like Spec# have introduced declarations for non-null references. This is the solution, which I rejected in 1965."
The infamous problem of null pointer errors (caused by 'null') is elegantly solved in languages with compile-time null-safety - they eliminate the risk for null pointer errors.
Erik Funkenbusch wrote: null is, at it's core, an abomination
Suppose that you have to invent a new programming language.
You don't want to use 'null' (or any variation like 'void', 'nothing', etc.) because you think it is an abomination.
What value would you then store in variable 'productDeliveryDate' if the product has not yet been delivered?
Erik Funkenbusch wrote: Bob would note the list is empty and never leave for the grocery store in the first place.
True.
I used this ridiculous behavior to illustrate what can happen in software that returns an empty list instead of 'null' (e.g. sending an empty list over a network, in order to 'do nothing'). For concrete source code examples please refer to part 2[^].
Erik Funkenbusch wrote: if you get an empty list, your foreach would simply do nothing and perform no work
The risk for bad outcomes, due to the fact that we simply iterate over the list and do nothing, instead of checking for emptiness and differentiating the cases "there are data' and 'there are no data' are explained in part 2[^]. This risk doesn't exist in languages that use 'null' and natively provide compile-time null-safety.
Erik Funkenbusch wrote: You can have Nothing<t> object for types that can return Nothing
Sorry, but I don't get this.
Could you please explain how your 'Nothing' object would be used and show us a source code example.
|
|
|
|
|
ChristianNeumanns wrote: The infamous problem of null pointer errors (caused by 'null') is elegantly solved in languages with compile-time null-safety - they eliminate the risk for null pointer errors.
I'm not sure anything "eliminates" the risk, though it certainly reduces it. Compile-time null checking is a hack IMO, to deal with a problem caused by the introduction of null reference pointers. Simply put, we should not be able to assign any value to a pointer that is not a valid memory address. To go even further, one should not be able to assign any value to a pointer that is not a validly pointed to object. More on this later.
ChristianNeumanns wrote: What value would you then store in variable 'productDeliveryDate' if the product has not yet been delivered?
As I mentioned in my last message, you have a "Nothingable" type (similar to the C# Nullable<t> type but useful for reference types as well). I don't really like the name, but don't have a better one at the moment. In your example, you have Date? which is not actually a null value, but indicates that it does not hold a valid value either. In this case, you can say productDeliveryDate.HasValue == true/false, or you can use coalescing operators like ?? or the new C# ?. operator to basically do nothing on an object with no value.
This is something we can more or less do today without language support (I am guessing we can override the ?? and ?. operators, if not it may require some support).
ChristianNeumanns wrote: The risk for bad outcomes, due to the fact that we simply iterate over the list and do nothing, instead of checking for emptiness and differentiating the cases
That is more of a methodology or process issue than anything else. If there is some logical difference between empty and non-existent then it's easy to differentiate them.
For instance, there is a difference between a Store with no inventory and no store at all. But you should not even be executing code to iterate over inventory if there is no store at all. You should not even be there. Null is a database concept because it represents the empty set of a join, or it represents the absence of data in a column. Null in program code represents "Something which shouldn't ever exist" and therefore is often used to indicate lack of existence.. End of pointers arrays, end of linked lists, end of files, uninitialized states, etc... all of those things can also be represented by explicit objects in an object oriented language, and with much greater semantic meaning.
If you're talking about a non-object oriented language like C or assembly, then sure... Null is probably your only choice for an indicator of something that can't otherwise exist as normal data, unless you basically design an OOP type system into your application. But in in almost any modern OOP language, null has outlived its usefulness in my opinion.
--
Where are we going? And why am I in this handbasket?
|
|
|
|
|
|
I agree that no result available vs empty collection are two different things, awareness of that is good. That being said, advocating null references - an already commonly abused concept - is very bad advice.
The problems stated in these examples don't justify null references and can be solved in much better and self-documenting ways. Eg Bob's postage stamps:
interface Person {
Optional<StampCollector> stampCollection();
}
interface StampCollector {
List<PostageStamp> postageStamps();
// other properties of a collector
}
What's the difference? Reading Person makes it obvious that not all people collect stamps, and need different handling. The type StampCollector is free to define it's invariants/constraints (you'll need more than the special case of having at least one element in some collection). No fiddling with special cases, null references, obscure/implicit tricks.
The same pattern can be applied to all the other examples similarly. Note that Optional/Maybe does not need language support as stated in the Note section. If it doesn't come out of the box, there are libraries for it.
Edit: html escape
modified 25-Jan-15 18:19pm.
|
|
|
|
|
mrcellux wrote: The problems stated in these examples don't justify null references and can be solved in much better and self-documenting ways
Really?
mrcellux wrote: <layer>Reading Person makes it obvious that not all people collect stamps, and need different handling.
Yes, this is a clear advantage of the Optional pattern - your intention is clear.
However we have the same advantage with 'null' in a null-safe language (which I advocate in my articles). The 'nullable' keyword documents our intention:
public nullable List<Stamp> postageStamps();
What's more, the 'nullable' keyword informs the compiler that returning 'null' is allowed and ensures we can't forget to make a distinction between the two semantically different cases 'there are stamps' and 'there are no stamps' (i.e. client code must check for 'null').
On the other hand, misuses of the Optional pattern (as implemented in Java) cannot be prevented by the compiler. For more information please refer to chapter 'The Optional/Maybe Pattern' in my previous article Why We Should Love null[^]
For example, nothing can prevent a class that implements interface 'Person' to accidentally return 'null';
Another advantage of the 'nullable' keyword is that this important information can be retrieved and used by tools (IDE, static code analyzers, etc.) in a reliable way.
mrcellux wrote: No fiddling with special cases, null references, obscure/implicit tricks.
With the solution you suggest we have to check 4 things if we really want to be on the safe side:
1. check if stampCollection() returns null
2. check if the Optional returned by stampCollection() contains a value
3. check if postageStamps() in StampCollector returns null
4. check if postageStamps() returns an empty list
And there is no compiler support for reminding us to do these checks if we omit them.
Now compare this with the null-safe version: There is no ambiguity in the API. There is only a single check to be done by client code (e.g. if stampList is null then ...). And the compiler ensures we don't forget this check.
mrcellux wrote: Note that Optional/Maybe does not need language support as stated in the Note section.
Where did you see this ('stated in the Note section')?
As far as I know I never ever said that the Optional/Maybe needs language support.
mrcellux wrote: If it doesn't come out of the box, there are libraries for it.
Yes, and if there is no library support then we can very easily create an Optional type ourselves (if we really need it).
|
|
|
|
|
To your list of 1-4 i could add a fifth: even the Optional reference could be null. And this is my point. Systems relying on null as an encouraged option turn into a NullPointerException minefield, and developers often end up in speculative null checking frenzy.
Consider avoiding null as a convention - if forced to for some reason, document nullability as explicitly as possible. In such a context the code reads differently. We see that a Person may or may not be a StampCollector. We can also see what information we have on a stamp collector. We don't have to guess or look up what references may be null and check them - we can assume none of them are. The code should express this information, using null references in eg Java hides this.
Making StampCollector an explicit domain concept has some more benefits too. If being a collector means the person must have at least one stamp, it can be made a StampCollector class invariant. Note that constraining some list to have at least one element is a very special case, there is a lot more flexibility you'll need in real systems to express the domain. You can't often do that with special references.
|
|
|
|
|
The entire premise of this article is misleading, and absurd.
First of all, NULL != No Results in programming as many have pointed out. It means a pointer without a destination. Now the semantics of that value has slightly changed over time, but let's not confuse the NULL DB value with a NULL programming value. In addition, there is absolutely no correlation between a real world list, and a NULL pointer. You cannot make a NULL list in real life. If you wanted to get close, asking your wife for a list, and making somewhere to store it, but not having her response yet would perhaps come close.
Second, since NULL means an uninitialized pointer, it holds a critically important role that you are steamrolling by using it for something it was not designed for. I love using NULL when appropriate, so don't think I am discouraging it, I just am pointing out it's correct usage.
With your suggested pattern I am shackled without the expressive ability I have without it.
Consider this scenario:
I have a list of Allergies that takes some time to compute.
Because of this I only want to compute them when I need them.
Sometimes I may not have any allergies
Once I know the allergies I shouldn't calculate them again.
With your pattern, I cannot complete this without significant kludge and additional tracking overhead.
Using NULL correctly, I can know the list is uninitialized, calculate the allergies, and know that no matter how many results are there, if I have a list, I do not need to reload. Basic Lazy loading.
List<allergy> allergies = NULL;
...
If(allergies == NULL)
allergies = GetAllergies();
DoStuffUsingAllergies(allergies);
|
|
|
|
|
Member 10433332 wrote: NULL != No Results in programming
I never said the opposite (or at least I didn't intend to say the opposite). We can think of null as 'the absence of data', and this is a valuable piece of information. A function that returns 'null' tells us that there are ... well, no data, which is a result (e.g. a function that returns null for a delivery date tells us that the delivery date is unknown, and this is valuable information).
Member 10433332 wrote: let's not confuse the NULL DB value with a NULL programming value
Could you tell us why you think that the concept of null in a DB is different from null in code?
Member 10433332 wrote: there is absolutely no correlation between a real world list, and a NULL pointer
The correlation is this: Null in code typically means that there is no list in real life. For example, if variable to_do_list points to null it means that there is no to do list (or the list is unknown).
Member 10433332 wrote: NULL means an uninitialized pointer
Sorry, but I fully disagree. A variable that points to null is initialized (because it points to null). And the compiler should track this. An uninitialized variable cannot be used in an expression, but a variable that points to null can very well be used. That's why the following Java code compiles:
String s = null;
System.out.println ( s );
But this code generates a compilation error:
String s;
System.out.println ( s );
Member 10433332 wrote: NULL ... holds a critically important role that you are steamrolling
Maybe I misunderstand, but I wonder why you think I am steamrolling null. Exactly the opposite is true. That's why I chose Why we should love 'null'[^] as title for my first article.
Member 10433332 wrote: With your pattern, I cannot complete this without significant kludge and additional tracking overhead.
Sorry, but I don't get your point.
And I can't understand the null check in your example:
List allergies = NULL;
...
If(allergies == NULL)
allergies = GetAllergies();
|
|
|
|
|
ChristianNeumanns wrote: I never said the opposite (or at least I didn't intend to say the opposite). We can think of null as 'the absence of data', and this is a valuable piece of information. A function that returns 'null' tells us that there are ... well, no data, which is a result (e.g. a function that returns null for a delivery date tells us that the delivery date is unknown, and this is valuable information).
Again, NULL is not the absence of data. It is like I said earlier, a pointer with no destination. This can easily be used to know that we do not yet know an answer, but it does not definitively tell us that the result was that there is nothing, especially for lists.
ChristianNeumanns wrote: Could you tell us why you think that the concept of null in a DB is different from null in code?
NULL in programming is a pointer that has no destination. NULL in a DB is a special value specifically reserved to indicate "I don't know!". A null BirthDate in the DB for a customer, for example, indicates we don't know the birthdate. Obviously, there is a birthdate (otherwise wouldn't be a customer) but *we don't know it*. You can use the same concept programmatically if you so choose, (To treat a NULL pointer/value as unknown), and I often do. However it is important to understand at the root of it all, NULL in code simply means uninitialized.
ChristianNeumanns wrote: The correlation is this: Null in code typically means that there is no list in real life. For example, if variable to_do_list points to null it means that there is no to do list (or the list is unknown).
AHA! Your starting to see the issue! Which is it, no to-do list, an unkown to-do list, or a now empty to-do list. You have lost that expressiveness by misusing NULL.
ChristianNeumanns wrote: Sorry, but I fully disagree. A variable that points to null is initialized (because it points to null). And the compiler should track this. An uninitialized variable cannot be used in an expression, but a variable that points to null can very well be used. That's why the following Java code compiles:
First of all, there are a ridiculous number of programming languages and some that do many things odd, so I wouldn't use one languages behavior as a bedrock of how things should be done anywhere but that specific language. Having said that, I hope you realize that is a code convenience feature by the JAVA compiler for local variables (since it can be ambiguous for the next person maintaining the code) that it requires local variables to be defined. Make an array of object values, and I think you will see without initializing the object pointers, they are all NULL. Moreover, I have worked in a large number of languages, most of which work that way ( most of which allow you to be ambiguous about local variables as well).
ChristianNeumanns wrote: Sorry, but I don't get your point.
And I can't understand the null check in your example:
Sorry, not JAVA code there. Psuedo C#. The point was that later in code when you want to know the allergies you can check for NULL, and load the allergies if they have not been loaded. If we return an empty list when the person has no allergies, we can now tell the difference between "I have no allergies" and "I don't know if I have any allergies" without any additional logic, or hackish additional variables.
ChristianNeumanns wrote: Maybe I misunderstand, but I wonder why you think I am steamrolling null. Exactly the opposite is true. That's why I chose Why we should love 'null'[^] as title for my first article.
Because you are making NULL lose syntactical meaning by overusing it.
|
|
|
|
|
I think you are way to dogmatic about this.
4 Articles about one single issue... I don't get it.
Anyway:
I'm programming in the .Net world and because of that, I try to follow the guidelines and best practices of the framework.
At least it should be easier for new colleagues to read an understand my code if I follow the standards.
So my question is: What is the standard in the .Net world?
Look here:
http://msdn.microsoft.com/de-de/library/dn169389(v=vs.110).aspx
"X DO NOT return null values from collection properties or from methods returning collections. Return an empty collection or an empty array instead."
Other platforms or frameworks may require other standards and special situations may require a different solution.
|
|
|
|
|
Malte Klena wrote: I try to follow the guidelines and best practices of the framework.
That's good and that's exactly what I suggested already in the final conclusion of part 2[^]:
"For example, if we work in an environment where 'return an empty collection and not null' is the rule and applied by all members of the team, then we should do the same, even if we are convinced that returning null is better. Swimming against the tide creates inconsistencies and leads to other evil problems."
Malte Klena wrote: What is the standard in the .Net world? Look here: http...
That's exactly the link I also mentioned already in part 1[^] (see section "What's the Problem?")
So, while this is the standard which every developer working in this environment should respect and apply, I (and many other people) firmly believe that there is a better standard. The reasons are explained in part 2[^].
|
|
|
|
|
I still disagree with the premise of using null in this manner, but I also feel the Shopping List example is a very poor one. If software does the digital equivalent of driving to the store with an empty shopping list, then the code is inefficient and incomplete. Just as if Bob drives to the store with an empty list, he's lazy and/or stupid.
I still don't see the point of using null for anything other than an uninitialised variable in any but the most speed/memory-sensitive applications. *shrug*
|
|
|
|
|
DevKnightlie wrote: If software does the digital equivalent of driving to the store with an empty shopping list ...
I saw software that sends empty lists over a distant network connection in order to 'do nothing'.
There are many other real-life examples of software doing crazy things.
DevKnightlie wrote: ... then the code is inefficient and incomplete
True.
DevKnightlie wrote: Just as if Bob drives to the store with an empty list, he's lazy and/or stupid
Yes.
But aren't we all sometimes lazy and/or stupid? If we weren't 'stupid' we would be able to write bug-free code - but reality shows we are still very far away from 'number_of_bugs = 0'. As for myself I am often ashamed when I re-read my lousy code written in the past (e.g. 6 months ago), although I love coding and do it for over two decades.
What we really need to protect us from ourselves, therefore, are fail-fast features natively built into the languages and standard libraries. Compile-time null-safety (which I advocate in my articles) is just one example of such features.
DevKnightlie wrote: I still don't see the point of using null for anything other than an uninitialised variable ...
A variable that points to 'null' is not uninitialized. It has a value, namely 'null'. The meaning of 'null' depends on the context. It is a valuable information.
The usefulness of 'null' is explained in part 2[^] and part 4[^], as well as in Why we should love 'null'[^].
Cheers
|
|
|
|
|
Well written article, but I am afraid I have to agree with the other critiques here. I just checked my current project and I have 800 different Lists in source code, having to add null checks to each user of any of these is ridiculously complex and unnecessary. In that whole code base (about 77k lines of code) there are only 264 null checks (and 80% of those are in tests or framework specific code) and I am pretty proud of that fact.
Also how could your advice be used in a language that does not have null (like functional programming languages)?
|
|
|
|
|
DeltaEngine wrote: Well written article
Thank you.
DeltaEngine wrote: I have 800 different Lists in source code, having to add null checks to each user of any of these is ridiculously complex and unnecessary
In null-safe languages (which I advocate in my articles) there are two cases:
1. lists that can be null, e.g.
nullable List<Order> getCustomerOrders ( String CustomerID )
2. lists that cannot be null, e.g.
List<ComputerUser> getComputerUsers()
Lists that cannot be null don't need to be checked for null, so you wouldn't have to add null checks for all of your 800 different lists. The compiler (of a null-safe language) even emits an error if we accidentally check for null, because it doesn't make sense.
On the other hand, lists that can be null have to be checked in the source code (verified by the compiler), and there is a good reason for this: If a list can be null then it means there are two semantically very different cases that have to be treated differently (at least in most situations). Some examples:
- if there are no children in the classroom then we have to switch off the lights, close the windows, etc.
- if the patient has allergies then he/she must be treated differently
- etc. etc.
As explained in part 2[^] it is often crucial to differentiate both cases - forgetting to do so can lead to harmful outcomes (see examples in part 2). There are rare situations in which it is not necessary to differentiate. But being nevertheless forced to check for null (in null-safe languages) leads to code that clearly documents our intention to treat both cases the same way.
To sum up:
In a null-safe language the compiler enforces the following:
- we can't forget to check for null if it is necessary to do so (i.e. nullable type)
- we can't check for null if it wouldn't make sense (i.e. non-nullable type)
This is the ideal solution and leads to more reliable code.
DeltaEngine wrote: how could your advice be used in a language that does not have null
Every language needs some way to express 'the absence of a value' (e.g. the product's delivery date is unknown).
Many languages use null, but some languages (mostly functional programming languages) use the Maybe/Optional pattern.
Please refer to my previous article Why we should love 'null'[^] for a discussion of alternatives to 'null' (and have a look at chapter 'The Optional/Maybe Pattern').
Cheers
|
|
|
|
|
Hey, would be nice to insert the links to the other parts in this one as well
|
|
|
|
|
Message Closed
modified 2-Feb-15 22:59pm.
|
|
|
|
|
Coding is not meant to simulate "real life" in a lot of cases, and an empty list seems like more appropriate in places such as getting a list of customers in a place. We are not giving that to a boss. I don't disagree that in some cases, returning null may have some meaning, but always returning null instead of an empty list does usually not make sense.
|
|
|
|
|
Elrond wrote: Coding is not meant to simulate "real life" in a lot of cases
I think that quite the opposite is true in many cases, especially in high level languages.
A lot of applications model the real-world. Some examples:
- search the net for terms like 'software simulation examples'
- a typical business application, like an ERP, models objects (data and behavior) of the real world, such as customers, suppliers, products, invoices, etc.
- neural networks software applications are inspired by real-world biological neural networks (see Wikipedia)
- the laws of nature applied in all kinds of scientific software applications, games, etc.
As I said already in another comment:
Asking myself
"How would this be done in real-life"
often helped me whenever I struggled to find a good design for data and behavior.
|
|
|
|
|
See more elaborate post below.
|
|
|
|
|
It's up to the API designer to think about what return values he wants, this is not really limited to lists. In generally I would aim for simplicity, and therefore not return null results if possible, so it saves the caller some exceptions. Usually things won't crash when dealing with empty lists, but just run fine as there's nothing in the list to process.
Sometimes you want non-value types to behave like value types. E.g. string, use string.Empty as an empty value and save yourself NullReferenceException headaches. Lists are the same. You can use e.g. use IList<> as a return value, and have a static array as an empty value (immutable), so you are not forced to create an empty list every time (so equal performance to returning null, analogous to string.Empty). Alternatively you could even make your own (generic) empty list implementation, again using a singleton pattern.
Sometimes you want value types to also have a null option, hence the nullable types (although still value types).
Altogether, compilers should become a bit more powerful so the API designer can specify whether returned non-value type can return null or not. Maybe this is already possible in recent compiler versions? I haven't really followed the recent changes in C#.
Wout
|
|
|
|
|
|