When calling Any() on a null object, it throws an ArgumentNullException in C#. If the object is null, there definitely aren't 'any', and it should probably return false.
Why does C# behave this way?
When calling Any() on a null object, it throws an ArgumentNullException in C#. If the object is null, there definitely aren't 'any', and it should probably return false.
Why does C# behave this way?
Any()
is asking: "Does this box contain any items?"
If the box is empty, the answer is clearly no.
But if there is no box in the first place, then the question makes no sense, and the function complains: "What the hell are you talking about? There is no box."
When I want to treat a missing collection like an empty one, I use the following extension method:
public static IEnumerable<T> OrEmpty<T>(this IEnumerable<T> sequence)
{
return sequence ?? Enumerable.Empty<T>();
}
This can be combined with all LINQ methods and foreach
, not just .Any()
.
With modern C#, you can easily handle the OP's scenario with a simple check like this:
List<string> foo = null;
if (foo?.Any() ?? false)
{
DoStuff();
}
This is kinda like a lame AnyOrDefault(bool default)
implementation that the OP is expecting the Any()
extension method to do.
You could easily make this into an extension like this:
public static bool HasItems<T>(this IEnumerable<T> source)
{
return (source?.Any() ?? false);
}
Honestly, I don't really like the name Renamed to AnyOrDefault
for this since it won't ever make sense to pass in a default value (a default of true would probably be pretty mean to people reading code later).HasItems
, as suggested in the comments. This is a far better name!
When dealing with reference types, a null
value is semantically different from an "empty" value.
A null
string is not the same as string.Empty
, and a null
IEnumerable<T>
is not the same as Enumerable.Empty<T>
(or any other "empty" enumerable of that type).
If Any
were not an extension method, calling it on null
would result in NullReferenceException
. Since it is an extension method, throwing some exception (although not necessary) is a good idea because it preserves the well-known semantics of trying to call a method on null
: BOOM!
Any()
is an extension method, so this
is actually passed as the first argument to the method. In this situation, it's understandable for it to throw ArgumentNullException
is this
is null
.
You can perform the check yourself beforehand:
bool hasAny = yourData == null ? false : yourData.Any(yourPredicate);
The Any
method runs against an IEnumerable
and tells you whether there are any items in the Enumerable. If you don't give it anything to enumerate then an ArgumentNullException is reasonable: a collection with no (matching) elements is different to no collecion.
Because Any() it is a extension method like this:
public static bool Any(this IEnumerable enumerable)
{
if (enumerable == null)
throw ArgumentNullException("enumerable");
...
}
As others have already mentioned, Any
checks whether or not a sequence contains elements. It does not prevent you from passing null
values(what might the bug in the first place).
Every extension method in Enumerable
class throws an an ArgumentNullException
if the source
is null
. Throwing ArgumentNullExceptions
in extensions actually is good practise.
Any()
internally first checks that if the source is null
, if so, throws an ArgumentNullException
(Does not return a bool in this case). Otherwise, it attempts to access the underlying sequence (IEnumerable
). If at least one element exists in the sequence, than true is returned, otherwise false. The thing to note here is technically there is three returns, true/false + throwing an ArgumentNullException
that could be caught (something that can be caught, hence technically a return).
At first, it may sounds like it should just return false if the source
is null since in a way could mean none.
However, there is different levels of information and behavior occurring here. What does no elements and null really mean?
Any()
true
if at least one element exists in the sequence.false
when a collection with 0 elements.NullReferenceException
when the sequence (underlying IEnumerable type) is null.null
in all contexts simply means nothing; no data, no memory pointer. null
is not a collection, nor an object. It represents an absence in memory. When the computer is "reading memory", it is attempting to read values stored in memory from a particular location. If the memory reader operation encounters the special null character aka 'null terminator' \0
, the CPU knows it cannot read this part in memory. Null also means no memory to describe an object or value of some kind. In your example, the underlying IEnumerable
object you are calling Any()
on doesn't exist, just a null terminator \0
. Attempting to read a memory address thats contains only a null terminator will throw a fatal exception. A NullReferenceException
if unhandled will terminate every program as defined by the operating system or other software layer(s) right below the application you're running.3
Any()
on a collection with 0
elements is still a collection.null
isn't a collection, nor object, nor value. Just nothing.If you have a wallet) that can hold money inside (the underlying sequence), with no cash (elements) inside, you have a wallet, but your broke (no elements). Null means you don't even have a wallet to put cash inside. The wallet and the cash do not exist.
Attempting to access something doesn't exist should fatally crash a program because its impossible. Something impossible can't be executed. This is true in the case of many, if not all languages.
As @msteel9999 mentions, in either scenario, you're still broke. :D
Any()
is an extension method that throws ArgumentNullException
if the source is null. Would you perform an action on nothing? In general, it's better to get some explicit indicator of what's happening in the code rather than the default value.
But it doesn't mean it can't be like that. If you know what you doing, write your own custom implementation.
I just wanted to share with you some practical advice my company is following.
We write our custom packages shared with private NuGet that are widely used in our products. Checking if the list is null/empty is very frequent, so we decided to write our implementation of Any
which makes our code shorter and simpler.
There is a method in the .Net Framework for this, why they hid it here, who knows...
namespace Microsoft.IdentityModel.Tokens
{
/// <summary>
/// A class which contains useful methods for processing collections.
/// </summary>
public static class CollectionUtilities
{
/// <summary>
/// Checks whether <paramref name="enumerable"/> is null or empty.
/// </summary>
/// <typeparam name="T">The type of the <paramref name="enumerable"/>.</typeparam>
/// <param name="enumerable">The <see cref="IEnumerable{T}"/> to be checked.</param>
/// <returns>True if <paramref name="enumerable"/> is null or empty, false otherwise.</returns>
public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
{
return enumerable == null || !enumerable.Any();
}
}
}