Null conditional operator to "nullify" array element existence
It doesn't support indexing safety because, when you get down to it, an indexer really is just syntactic sugar for any other type of method.
For example:
public class MyBadArray
{
public Customer this[int a]
{
get
{
throw new OutOfMemoryException();
}
}
}
var customers = new MyBadArray();
int? count = customers?[5]?.Orders?.Count();
Should this be caught here? What if the exception was more sensible, similar to a KeyNotFoundException, but specific to the type of collection we're implementing? We'd have to continually update the ?.
functionality to keep up.
Further, ?.
does not catch exceptions. It prevents them.
var customer = customers?[5];
is actually compiled as:
Customer customer = null;
if (customers != null)
customer = customers[5];
Making it catch exceptions becomes exceptionally more difficult. For example:
void Main()
{
var thing = new MyBadThing();
thing.GetBoss()?.FireSomeone();
}
public class MyBadThing
{
public class Boss
{
public void FireSomeone()
{
throw new NullReferenceException();
}
}
public Boss GetBoss()
{
return new Boss();
}
}
If it were simply catching exceptions, it would be written as :
Boss boss = customer.GetBoss();
try
{
boss.FireSomeone();
} catch (NullReferenceException ex) {
}
Which would actually catch the exception within FireSomeone
, rather than the null reference exception which would be thrown if boss were null.
The same bad-catching problem would be present if we were to catch index lookup exceptions, key not found exceptions, etc.
customers?.FirstOrDefault()?.Orders?.Count();
No zeroeth, no problem.
If you want to get the nth element without having NullReference or IndexOutOfRange exceptions, you can use:
customers?.Skip(n)?.FirstOrDefault()
No, because it is a null-conditional operator, not an indexoutofrange-conditional operator and is merely syntactic sugar to something like the following:
int? count = customers?[0]?.Orders?.Count();
if (customers != null && customers[0] != null && customers[0].Orders != null)
{
int count = customers[0].Orders.Count();
}
You can see that if there is no zeroth customer, you will get your regular IndexOutOfRangeException
.
One way you could work around it is to have an extension method that checks for the index and returns null if it doesn't exist:
public static Customer? GetCustomer(this List<Customer> customers, int index)
{
return customers.ElementAtOrDefault(index); // using System.Linq
}
Then your check could be:
int? count = customers?.GetCustomer(0)?.Orders?.Count();