43

When you create an extension method you can, of course, call it on null.But, unlike an instance method call, calling it on null doesn't have to throw a NullReferenceException -> you have to check and throw it manually.

For the implementation of the Linq extension method Any() Microsoft decided that they should throw a ArgumentNullException (https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/AnyAll.cs).

It irks me to have to write if( myCollection != null && myCollection.Any() )

Am I wrong, as a client of this code, to expect that e.g. ((int[])null).Any() should return false?

Buh Buh
  • 293
  • 1
  • 3
  • 9
thisextendsthat
  • 535
  • 1
  • 5
  • 11
  • 3
    This is too opinionated for this site, however I too am constantly annoyed about checking for null when I treat a null collection and empty collection exactly the same: Don't do anything with it. However, this breaks down when a null pointer is meaningful versus an empty collection (hide search results if null versus "Your search returned no results"). – Greg Burghardt Sep 18 '18 at 17:11
  • 44
    in C# 6, you can simplify your check to if (myCollection?.Any() == true) – 17 of 26 Sep 18 '18 at 17:26
  • 6
    Even in F#, which does not really use nulls except for interoperability with other .NET languages, `null |> Seq.isEmpty` throws `System.ArgumentNullException: Value cannot be null`. The expectation seems to be that you won't be passing undefined values for something that is expected to exist, so it's still an exception when you have a null. If it's a matter of initialization, I would start with an empty sequence instead of a `null`. – Aaron M. Eshbach Sep 18 '18 at 18:02
  • Having nullable reference types should alleviate this problem: https://msdn.microsoft.com/en-us/magazine/mt829270.aspx?f=255&MSPPError=-2147217396 – Matthew Sep 18 '18 at 19:38
  • 21
    That's why you should not return `null` when dealing with collections but instead use an empty collection in those cases. – Bakuriu Sep 18 '18 at 21:13
  • 31
    Well why is it null in the first place? You don't want null to be counted as empty, because it's not empty, it's something different entirely. Maybe you want it to be counted as empty in your case, but adding that sort of thing to the language leads to bugs. – user253751 Sep 18 '18 at 23:05
  • 1
    Complete aside: don't even write code like that `Error.ArgumentNull` function. That is really terrible stuff. Screws with the stack trace and is harder to read. Just throw the exception in line. – jpmc26 Sep 19 '18 at 06:54
  • 23
    I should point out that *every single* LINQ method throws on a null argument. `Any` is just consistent. – Sebastian Redl Sep 19 '18 at 08:01
  • 4
    In my opinion, the fact that when extension methods were added to C# that `foo` was certain to be non-`null` (assuming no concurrent manipulation) after `foo.Bar();` suddenly ceased being true was a fairly insidious change. I'm generally of the opinion that you should write extension methods such that they behave like "real" methods and thus throw an exception if the `this` argument is null. – Derek Elkins left SE Sep 19 '18 at 08:28
  • 1
    Even the inventor of the null pointer/reference calls it his [billion dollar mistake](https://en.wikipedia.org/wiki/Null_pointer#History). In my experience so far, there's indeed always a better solution than a null reference. From that perspective, making a language's usage of null more convenient will decrease the quality of the code that will be written with it. – R. Schmitz Sep 19 '18 at 09:38
  • Create an extension method `IList EmptyIfNull(this IList that){if(that==null) return new List(); return that;}` (Or IEnumerable) Then you can say `thing.EmptyIfNull().Any()` – Ben Sep 19 '18 at 09:49
  • 1
    @17of26: Is `== true` really needed? – Eric Duminil Sep 19 '18 at 11:28
  • 2
    @EricDuminil Yes, because the expression 'myCollection?.Any()' has three possible states: true, false, null – 17 of 26 Sep 19 '18 at 12:17
  • @17of26: Thanks for the answer. I thought `null` would be falsy in C#. – Eric Duminil Sep 19 '18 at 12:21
  • @thisextendsthat I'm curious what the code looks like that returns a null collection. I think the problem is not in the behavior of `Any()`, but in the behavior of whatever method or property you're getting the null from. There's really never a good reason for a collection-returning member to return null. – Kyralessa Sep 19 '18 at 14:04
  • 3
    Don't forget that an [extension method](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods) is actually a static method that is "added" to an existing type, and receives the calling instance as a parameter. So, when you do `myCollection.Any()` what you're actually doing is `MyCollectionType.Any(myCollection)`; hence the `ArgumentNullException` logic. – Josh Part Sep 19 '18 at 18:25
  • 4
    Two thoughts: 1. Perhaps your overall design could be reworked so you don't have to do an additional `null` check beforehand? In other words, methods which return `IEnumerable` should never return `null`, but rather an empty collection if that's the condition. 2. Create an extension method and use it instead: `public static bool IsNullOrEmpty(this IEnumerable source) { return source?.Any() != true; }` – Jesse C. Slicer Sep 19 '18 at 18:32
  • @JesseC.Slicer It depends on how your code is organized. If you invoke a function that returns the `IEnumerable` and then immediately process it in the same scope, you can rely on the method behaving according to its contract. However, if it's an *argument* to another function, that function should not rely on callers never passing in `null`. I certainly would not avoid creating additional functions just to avoid having to consider the `null` case. So this problem will inevitably come up in some places. – jpmc26 Sep 19 '18 at 23:58
  • @jpmc26 the `throw Error.XXX` code is completely fine and sensible. The stack trace isn't populated until the exception is thrown so there's nothing unusual about the stack traces. Refactoring out boilerplate exception creation increases readability and eases future changes. – Johnbot Sep 20 '18 at 07:09
  • Note that [CA2201](https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2201-do-not-raise-reserved-exception-types?view=vs-2017) considers NullReferenceException as reserved for the runtime. It should only be thrown when null is actually dereferenced. You should not throw it explicitly from your own code. – Wim Coenen Sep 20 '18 at 08:58
  • what @JoshPart said. – Tsahi Asher Sep 20 '18 at 10:31
  • 1
    Equivalent question on SO: [Why doesn't Any() work on a c# null object](https://stackoverflow.com/questions/11538243/why-doesnt-any-work-on-a-c-sharp-null-object) – CodesInChaos Sep 20 '18 at 17:28
  • @SebastianRedl: Even before LINQ, the C# `foreach` loop choked on `null` collections, so you can count that towards "consistency". – dan04 Sep 20 '18 at 23:28
  • There's consistency reasons, but let's explore from a EDD standpoint. When a dev calls Any on a collection, they are looking for matches, what's the point of looking for matches on a null, what if your collection is null erroneously? Should Any add to the problem? Regardless, the questions already been answered in the github ;) – RandomUs1r Sep 21 '18 at 16:52

13 Answers13

162

I have a bag with five potatoes in it. Are there .Any() potatoes in the bag?

"Yes," you say. <= true

I take all of the potatoes out and eat them. Are there .Any() potatoes in the bag?

"No," you say. <= false

I completely incinerate the bag in a fire. Are there .Any() potatoes in the bag now?

"There is no bag." <= ArgumentNullException

Dan Wilson
  • 3,073
  • 1
  • 12
  • 16
  • 4
    But vacuously, there are no potatoes in the bag? False implies False... (I don’t disagree, just a second perspective). – D. Ben Knoble Sep 21 '18 at 12:57
  • 7
    more practically, someone hands you a closed box from an unknown source. Does the bag in the box contain any potatoes? I am only interested in 2 scenarios - A) there is a bag in the box that has potatoes in it, or B) there are no potatoes and/or no bag in the box. Semantically, yes, there is a difference between null and empty, but 99% of the time I don't care. – dave thieben Sep 21 '18 at 13:29
  • 10
    You ate five raw potatoes?? Seek medical attention immediately. – Oly Sep 21 '18 at 20:38
  • 4
    @Oly Dan cooked them in the fire, before eating and using that fire to incinerate the bag. – Ismael Miguel Sep 22 '18 at 13:32
  • @Oly Well, it doesn't state anywhere that these were raw potatoes to begin with. Admittedly, it IS weird to keep cooked potatoes in a bag, but who are we to judge? :P – MBender Sep 22 '18 at 18:23
  • 1
    A better analogy than a specific bag that has been destroyed, is if I'm asking you if there are any potatoes in the bag and I just haven't told you which bag I'm talking about. A `Bag` variable *reference* to the bag I'm asking. If the variable is actually `null`, then it's not that I'm referring to some sort of defective bag that isn't actually capable of containing potatoes anymore (which could be reasonably argued to contain zero potatoes), I'm failing to give a reference for a bag at all. Neither `true` nor `false` is a consistent answer; the question is defective. – Ben Sep 24 '18 at 03:57
  • @IsmaelMiguel why not set fire to the bag and use that to cook the potatoes? – jwenting Sep 24 '18 at 04:39
  • @jwenting That breaks the order of the events described – Ismael Miguel Sep 24 '18 at 05:15
  • @IsmaelMiguel not really, he could have burned the bag with the potatoes inside, ending up with fried potatoes and no bag :) – jwenting Sep 24 '18 at 05:30
  • 5
    @D.BenKnoble: Without having seen the trunk of my car, are you willing to testify that there are no bodies in the trunk of my car? No? What's the difference between checking the trunk and not finding anything, or not checking the trunk? Can't you just testify now, since you would've seen the exact same amount of bodies in either case? ... That's the difference. You can't vouch for something if you're not able to actually confirm it in the first place. You assume that the two are equatable, but that's not a given. In some cases, there may be an important difference between the two. – Flater Sep 24 '18 at 12:35
  • 2
    If my hand (`Bag potatoes;`) contains nothing (`potatoes = null;`), and I point to it and ask "Are there any potatoes are in this bag?" (`if (potatos.Any()){}`). I think that you would be perfectly justified in looking at me strangely and saying "What bag? There is no bag there you crazy git! Go away!" (Throwing an exception). – Bradley Uffner Sep 24 '18 at 12:39
54

First off, it appears that that source code will throw ArgumentNullException, not NullReferenceException.

Having said that, in many cases you already know that your collection is not null, because this code is only called from code that knows that the collection already exists, so you won't have to put the null check in there very often. But if you don't know that it exists, then it does make sense to check before calling Any() on it.

Am I wrong, as a client of this code, to expect that e.g. ((int[])null).Any() should return false?

Yes. The question that Any() answers is "does this collection contain any elements?" If this collection does not exist, then the question itself is nonsensical; it can neither contain nor not-contain anything, because it doesn't exist.

Mason Wheeler
  • 82,151
  • 24
  • 234
  • 309
  • Of course that is how it works, and OP clearly knows that, but semantically, a non-existing set, is an empty set. In any case, you have to argue that it semantically doesn't make sense, not how it works. – Chris Wohlert Sep 18 '18 at 18:53
  • 3
    @ChrisWohlert "semantically, a non-existing set, is an empty set". Perhaps in set theory this is true, but we aren't dealing with set theory here. – Mason Wheeler Sep 18 '18 at 18:55
  • Sure we aren't, but we are talking about what can be expected, and to answer that we have to appeal to some theory or convention? As Telastyn put it, it's the C# convention, though if you came from somewhere else, you might not expect that convention. It doesn't seem intuitive to me, that a missing set isn't empty. I like the *Having said that* part, I **very** rarely have to test my collections for null. – Chris Wohlert Sep 18 '18 at 19:02
  • 18
    @ChrisWohlert: Your intuition intrigues me. Do you also think that semantically, a non-existent person is an empty person, whose name is the empty string and age is zero and so on? And do you think that a set containing `null` is equivalent to a set containing the empty set (and vice versa)? – ruakh Sep 18 '18 at 19:15
  • @ruakh I don't know what constitutes an *empty person*, but I can imagine a set containing empty sets, yes. As in, `{{}, {}, {}, {}}`. However, this only counts when *T* is a collection. – Chris Wohlert Sep 18 '18 at 19:22
  • 17
    @ChrisWohlert: Obviously a set can contain the empty set; but you seem to believe that `{ null }` and `{ {} }` should be equivalent. I find that fascinating; I can't relate to it at all. – ruakh Sep 18 '18 at 19:26
  • @ruakh Only when *T* is a set – Chris Wohlert Sep 18 '18 at 19:28
  • And it is only equivalent to the question, *are there any items?* – Chris Wohlert Sep 18 '18 at 19:29
  • 9
    Should `.Add` on a `null` collection somehow create a collection for us too? – Matthew Sep 18 '18 at 20:24
  • No, it only pertains to the question of whether there are any items. – Chris Wohlert Sep 18 '18 at 20:42
  • 4
    @ChrisWohlert If `Add` shouldn't create a collection out of nothing, why should Any (and then tell you its empty)? Seems very inconsistent to me. – Andy Sep 18 '18 at 22:31
  • 13
    @ChrisWohlert "A is an empty set. B is a set containing a watermelon. Is A empty? Yes. Is B empty? No. Is C empty? Uhhh..." – user253751 Sep 18 '18 at 23:23
  • 16
    Pedantic point: It's not the case that, in set theory, a non-existing set is an empty set. The empty set exists, and any non-existing set is not it (and indeed, is not anything at all, since it doesn't exist). – James_pic Sep 19 '18 at 09:47
  • 1
    @ChrisWohlert If we want to get all set-theoretic, then (null).Any() should also return true. After all, every collection that we checked has at least one element. So now you have a call that return both true *and* false. – Richard Ward Sep 19 '18 at 14:35
  • `null` does not have a type @ChrisWohlert, otherwise you couldn't assign it to objects of incompatible types. `null` is not a `Set` or a `List` or anything else that could be empty; it does not matter what `T` is. – Matthew Read Sep 19 '18 at 15:47
  • I am not trying to say null is a set, only that where no set exists, no items exist. – Chris Wohlert Sep 19 '18 at 16:12
  • @ChrisWohlert "semantically, a non-existing set, is an empty set"--No. There is no such thing as a non-existent set. The text "a non-existing set" might be used with other words to say things, like something about there not existing a set with certain properties, but neither it nor it plus other words say anything about a non-existent set because no set is non-existent. – philipxy Sep 24 '18 at 11:32
22

Null means missing information, not no elements.

You might consider more broadly avoiding null — for example, use one of the built-in empty enumerables to represent a collection with no elements instead of null.

If you are returning null in some circumstances, you might change that to return the empty collection.  (Otherwise, if you're finding null's returned by library methods (not yours), that's unfortunate, and I would wrap them to normalize.)

See also https://stackoverflow.com/questions/1191919/what-does-linq-return-when-the-results-are-empty

Erik Eidt
  • 33,282
  • 5
  • 57
  • 91
  • 1
    That `null` doesn't mean *no elements* is a matter of using a sensible convention. Just think about native OLESTRINGS, where it actually *does* mean that. – Deduplicator Sep 18 '18 at 16:09
  • 1
    @Deduplicator, are you talking about Microsoft's Object Linking and Embedding perchance? If you are let's recall that OLE is from the '90s! Looking more currently, many modern languages (C#,Java,Swift) are providing facilities to eliminate/eradicate uses of null. – Erik Eidt Sep 18 '18 at 16:25
  • 2
    @ErikEidt They're doing that, in part, because `null` doesn't mean "missing information". `null` doesn't have a single meaningful interpretation. Instead, it is a matter of how you treat it. `null` is sometimes used for "missing information", but also for "no elements", and also for "an error occurred" or "uninitialized" or "conflicting information" or some arbitrary, ad-hoc thing. – Derek Elkins left SE Sep 22 '18 at 00:20
  • -1. This is a decision made by the developer, not by abstract theories. `null` is absolutely frequently used to mean "no data." Sometimes it makes sense. Other times, not. – jpmc26 Jan 18 '19 at 04:04
14

Aside from the null-conditional syntax, there is another technique to alleviate this problem: don't let your variable ever remain null.

Consider a function that accepts a collection as a parameter. If for the purposes of the function, null and empty are equivalent, you can ensure that it never contains null at the beginning:

public MyResult DoSomething(int a, IEnumerable<string> words)
{
    words = words ?? Enumerable.Empty<string>();

    if (!words.Any())
    {
        ...

You can do the same when you fetch collections from some other method:

var words = GetWords() ?? Enumerable.Empty<string>();

(Note that in cases where you have control over a function like GetWords and null is equivalent to the empty collection, it is preferable to just return the empty collection in the first place.)

Now you may perform any operation your wish on the collection. This is especially helpful if you need to perform many operations that would fail when the collection is null, and in cases where you get the same result by looping over or querying an empty enumerable, it will allow eliminating if conditions entirely.

jpmc26
  • 5,389
  • 4
  • 25
  • 37
13

Am I wrong, as a client of this code, to expect that e.g. ((int[])null).Any() should return false?

Yes, simply because you're in C# and that behavior is well defined and documented.

If you were making your own library, or if you were using a different language with different exception culture then it would be more reasonable to expect false.

Personally I feel as though return false is a safer approach that makes your program more robust, but it's debatable at least.

Telastyn
  • 108,850
  • 29
  • 239
  • 365
  • 15
    That 'robustness' is often an illusion - if the code that generates the array suddenly starts generating NULLs unexpectedly due to a bug or other issue, your 'robust' code will hide the problem. It's appropriate behaviour at times, but isn't a safe default. – GoatInTheMachine Sep 19 '18 at 12:41
  • 1
    @GoatInTheMachine - I disagree about it being a safe default, but you're right that it does hide the problem and that such behavior is undesirable at times. In my experience, hiding the problem (or trusting unit tests to catch other code's problems) is better than crashing your app by throwing unexpected exceptions. I mean really - if the calling code is broken enough to send in nulls, what's the chance they handle exceptions properly? – Telastyn Sep 19 '18 at 13:21
  • 10
    If something is broken, whether it's my code, another colleague's or a client's, I'd rather the code failed immediately and people knew about it than it carried on in a potentially inconsistent state for an undetermined amount of time. Being able to do this is of course sometimes a luxury and not always appropriate but it's the approach I prefer. – GoatInTheMachine Sep 19 '18 at 13:35
  • 1
    @GoatInTheMachine - I agree for lots of things, but collections tend to be different. Treating null as an empty collection is not a terribly inconsistent or surprising behavior. – Telastyn Sep 19 '18 at 17:22
  • 9
    Imagine you have an array that is populated from a JSON document received via a web request, and in production one of your API's clients suddenly has an issue where they send through partly invalid JSON causing you to deserialize an array as NULL, would you rather: the code, at worst, threw a NULL reference exception the moment the first bad request came in, which you'd see in logging and then immediately tell client to fix their broken requests, OR your code says "oh, the array's empty, nothing to do!" and silently wrote 'purchase basket had no items' to the db for a few weeks? – GoatInTheMachine Sep 20 '18 at 09:41
11

If the repeated null-checks annoy you, you could create your own 'IsNullOrEmpty()' extension method, to mirror the String method by that name, and wrap both the null-check and the .Any() call into a single call.

Otherwise, the solution, mentioned by @17 of 26 in a comment under your question, is shorter than the 'standard' method as well, and reasonably clear to anyone familiar with the new null-conditional syntax.

if(myCollection?.Any() == true)
Theo Brinkman
  • 227
  • 1
  • 2
  • 4
    You could also use `?? false` instead of `== true`. This would seem more explicit (cleaner) to me since it handles *only* the case of `null`, and it's not really any more verbose. Plus `==` checks for Booleans usually trigger my gag reflex. =) – jpmc26 Sep 19 '18 at 06:39
  • 2
    @jpmc26: I don't know much about C#. Why isn't `myCollection?.Any()` enough? – Eric Duminil Sep 19 '18 at 11:30
  • 6
    @EricDuminil Because of the way that the null-conditional operator works, `myCollection?.Any()` effectively returns a nullable-Boolean rather than an ordinary Boolean (that is, bool? instead of bool). There is no implicit conversion from bool? to bool, and it's a bool we need in this particular example (to test as part of the `if` condition). Therefore, we must either explicitly compare with `true` or make use of the null-coalescing operator (??). – Steven Rands Sep 19 '18 at 13:14
  • @jpmc26 ?? false is harder to read then myCollection?.Any() == true. Regardless of your gag reflex, == true is what you are trying to implicitly test. ?? false just adds additional noise and you still need to do the evaluation in your head about what happens if null, that null == true would fail, almost without the reader even having to be aware of the `?.`. – Ryan Leach Sep 21 '18 at 05:10
  • 2
    @RyanTheLeach I have to think much harder about whether `==` will actually work. I already know what happens with a Boolean intuitively when it can only be `true` or `false`; I don't even have to think about it. But throw `null` into the mix, and now I have to work out whether that `==` is right. `??` instead just eliminates the `null` case, reducing it back to `true` and `false`, which you already understand immediately. As for my gag reflex, when I first see it, my immediate instinct is to just delete it since it's usually useless, which you don't want happening. – jpmc26 Sep 21 '18 at 05:49
  • Agree to disagree then. – Ryan Leach Sep 21 '18 at 05:51
  • I've written real code in both ways, (x?.Any() == true) and (x?.Any() ?? false). When I come back to read the code later, they're both more difficult to read than (x.Any()) would be, but the ?? false always makes me stop to calculate what it's actually intending. I use ?? all over the place without problem, but for some reason, combining it with a bool makes it really hard. It gets really messy when you're checking !x.Any() instead of x.Any(), then you end up with something like !(x?.Any() ?? false) or (x?.Any() != false). None of those are clear at a glance... – Bryce Wagner Sep 24 '18 at 03:04
9

Am I wrong, as a client of this code, to expect that e.g. ((int[])null).Any() should return false?

If you wonder about expectations you have to think about intentions.

null means something very different from Enumerable.Empty<T>

As mentioned in Erik Eidt's answer, there is a difference in meaning between null and an empty collection.

Let's first glance at how they are supposed to be used.

The book Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, 2nd Edition written by Microsoft architects Krzysztof Cwalina and Brad Abrams states the following best practice:

X DO NOT return null values from collection properties or from methods returning collections. Return an empty collection or an empty array instead.

Consider your calling a method that is ultimately getting data from a database: If you receive an empty array or Enumerable.Empty<T> this simply means your sample space is empty, i.e. your result is an empty set. Receiving null in this context, however, would signify an error state.

In the same line of thinking as Dan Wilson's potato analogy, it makes sense to ask questions about your data even if it is an empty set. But it makes a lot less sense, if there is no set.

  • 1
    Whether they have different meanings is highly dependent on context. Often, for the purpose of whatever logic is at hand, they represent equivalent concepts. Not *always*, but often. – jpmc26 Sep 20 '18 at 21:39
  • 1
    @jpmc26: I think the point of this answer is to say that by coding guidelines for c# they do have a different meaning. You can always right code where null and empty are the same if you want to. And in some cases you may also do the same thing whether it is null or empty but that doesn't mean that they mean the same thing. – Chris Sep 21 '18 at 10:23
  • @Chris And I'm saying that coding guidelines from people who designed the language can't be used as a replacement for evaluating your code in the context of your requirements, the libraries you're using, and the other developers your work with. The idea that they'll never be equivalent in context is pie-in-the-sky idealism. They might even not be equivalent for the data at hand in general but be equivalent for generating a result in some specific function you're writing; in that case, you *need* to support both but ensure they generate the same result. – jpmc26 Sep 21 '18 at 15:38
  • I may be confused by what you are saying but when null and empty are equivalent for generating a result I don't see why you wouldn't just use separate is null and is empty checks rather than wanting a method that does both at once but doesn't allow you to distinguish them in other situations... – Chris Sep 21 '18 at 16:36
  • @Chris I'm referring to having different contexts (which often correspond to scopes). Consider a function for example. `null` and empty may result in the same return value for the function, but that doesn't mean that `null` and empty mean exactly the same thing in the calling scope. – jpmc26 Sep 22 '18 at 19:00
3

There are many answers explaining why null and empty are different and enough opinions trying to explain both why they should be treated differently or not. However you're asking:

Am I wrong, as a client of this code, to expect that e.g. ((int[])null).Any() should return false?

It's a perfectly reasonable expectation. You're as right as someone else advocating for the current behavior. I agree with current implementation philosophy but driving factors are not - only - based on out of context considerations.

Given that Any() without predicate is essentially Count() > 0 then what do you expect from this snippet?

List<int> list = null;
if (list.Count > 0) {}

Or a generic:

List<int> list = null;
if (list.Foo()) {}

I suppose you expect NullReferenceException.

  • Any() is an extension method, it should smoothly integrate with the extended object then throwing an exception is the least surprising thing.
  • Not every .NET language supports extension methods.
  • You can always call Enumerable.Any(null) and there you definitely expect ArgumentNullException. It's the same method and it has to be consistent with - possibly - almost EVERYTHING else in the Framework.
  • Accessing a null object is a programming error, framework should not enforce null as magic value. If you use it that way then it's your responsibility to deal with it.
  • It's opinionated, you think one way and I think another way. Framework should be as much unopinionated as possible.
  • If you have a special case then you must be consistent: you have to take more and more highly opinionated decisions about all the other extension methods: if Count() seems an easy decision then Where() is not. What about Max()? It throws an exception for an EMPTY list, shouldn't it throw also for a null one?

What library designers did before LINQ was to introduce explicit methods when null is a valid value (for example String.IsNullOrEmpty()) then they HAD to be consistent with existing design philosophy. That said, even if pretty trivial to write, two methods EmptyIfNull() and EmptyOrNull() might be handy.

Adriano Repetti
  • 587
  • 4
  • 13
1

Jim is supposed to leave potatoes in every bag. Otherwise I'm going to kill him.

Jim has a bag with five potatoes in it. Are there .Any() potatoes in the bag?

"Yes," you say. <= true

Ok so Jim lives this time.

Jim takes all of the potatoes out and eats them. Are there .Any() potatoes in the bag?

"No," you say. <= false

Time to kill Jim.

Jim completely incinerates the bag in a fire. Are there .Any() potatoes in the bag now?

"There is no bag." <= ArgumentNullException

Should Jim live or die? Well we didn't expect this so I need a ruling. Is letting Jim get away with this a bug or not?

You can use annotations to signal that you're not putting up with any null shenanigans this way.

public bool Any( [NotNull] List bag ) 

But your tool chain has to support it. Which means you likely will still end up writing checks.

candied_orange
  • 102,279
  • 24
  • 197
  • 315
0

If this bothers you so much, I suggest a simple extension method.

static public IEnumerable<T> NullToEmpty<T>(this IEnumerable<T> source)
{
    return (source == null) ? Enumerable.Empty<T>() : source;
}

Now you can do this:

List<string> list = null;
var flag = list.NullToEmpty().Any( s => s == "Foo" );

...and flag will set to false.

John Wu
  • 26,032
  • 10
  • 63
  • 84
0

This is a question about C#'s extension methods and their design philosophy, so I think that the best way to answer this question is to quote MSDN's documentation on the purpose of extension methods:

Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are a special kind of static method, but they are called as if they were instance methods on the extended type. For client code written in C#, F# and Visual Basic, there is no apparent difference between calling an extension method and the methods that are actually defined in a type.

In general, we recommend that you implement extension methods sparingly and only when you have to. Whenever possible, client code that must extend an existing type should do so by creating a new type derived from the existing type. For more information, see Inheritance.

When using an extension method to extend a type whose source code you cannot change, you run the risk that a change in the implementation of the type will cause your extension method to break.

If you do implement extension methods for a given type, remember the following points:

  • An extension method will never be called if it has the same signature as a method defined in the type.
  • Extension methods are brought into scope at the namespace level. For example, if you have multiple static classes that contain extension methods in a single namespace named Extensions, they will all be brought into scope by the using Extensions; directive.

To summarize, extension methods are designed to add instance methods to a particular type, even when the developers cannot do so directly. And because instance methods will always override extension methods if present (if called using instance method syntax), this should only be done if you cannot directly add a method or extend the class.*

In other words, an extension method should act just like an instance method would, because it may end up being made an instance method by some client. And because an instance method should throw if the object it's being called on is null, so should the extension method.


*As a side note, this is exactly the situation that the designers of LINQ faced: when C# 3.0 was released, there were already millions of clients that were using System.Collections.IEnumerable and System.Collections.Generic.IEnumerable<T>, both in their collections and in foreach loops. These classes returned IEnumerator objects which only had the two methods Current and MoveNext, so adding any additional required instance methods, such as Count, Any, etc., would be breaking these millions of clients. So, in order to provide this functionality (especially since it can be implemented in terms of Current and MoveNext with relative ease), they released it as extension methods, which can be applied to any currently existing IEnumerable instance and can also be implemented by classes in more efficient ways. Had C#'s designers decided to release LINQ on day one, it would have been provided as instance methods of IEnumerable, and they probably would have designed some kind of system to provide default interface implementations of those methods.

TheHans255
  • 132
  • 1
  • 11
0

I think the salient point here is that by returning false instead of throwing an exception, you are obfuscating information that may be relevant to future readers/modifiers of your code.

If it's possible for the list to be null, I would suggest having a separate logic path for that, as otherwise in the future someone may add some list based logic (like an add) to the else{} of your if, resulting in an unwanted exception that they had no reason to predict.

Readability and maintainability trumps 'I have to write an extra condition' every time.

0

As a general rule, I write most of my code to assume that the caller is responsible for not handing me null data, mostly for the reasons outlined in Say No to Null (and other similar posts). The concept of null is often not well understood, and for that reason, it's advisable to initialize your variables ahead of time, as much as practical. Assuming you're working with a reasonable API, you should ideally never get a null value back, so you should never have to check for null. The point of null, as other answers have stated, is to make sure that you have a valid object (e.g. a "bag") to work with before continuing. This isn't a feature of Any, but instead a feature of the language itself. You can't do most operations on a null object, because it does not exist. It's like if I ask you to drive to the store in my car, except I don't own a car, so you have no way to use my car to drive to the store. It's nonsensical to perform an operation on something that literally does not exist.

There are practical cases for using null, such as when you literally do not have the requested information available (e.g. if I asked you what my age is, the most likely choice for you to answer with at this point is "I don't know", which is what a null value is). However, in most cases, you should at least know if your variables are initialized or not. And if you don't, then I would recommend that you need to tighten up your code. The very first thing I do in any program or function is to initialize a value before I use it. It is rare that I need to check for null values, because I can guarantee that my variables are not null. It's a good habit to get in to, and the more frequently you remember to initialize your variables, the less frequently you need to check for null.

phyrfox
  • 529
  • 3
  • 6