Handling null objects when calling methods
Asked Answered
A

6

7

I have been looking around finding the best possible option for handling null objects in calling a method (or method chain).

It is our common practice to check with if condition:

if ( customObject != null ) {
    customObject.callMe();
}

Which can be further improved by using extension methods:

Program customObject = null;
if (customObject.NotNull()) {
    customObject.CallMe();
}

public static bool NotNull(this object o) {
    return o == null;
}

PLEASE NOTE: I normally ignore ! from my programming practice. Hence it is wise to say that for me extension methods are good to go.

However, it becomes very complicated in dealing with when the Method chain is involved.

customObject.CallMe().CallMe2() ex... 

How you do think it can be handled in C#, so that the CallMe is called only if customObject is not null and CallMe2 is called only if CallMe returns non null object.

Of course I can use If conditions or ternary operator. However, I would want to know if vNext, C#5.0 has some thing to offer.

Aftergrowth answered 31/8, 2014 at 23:50 Comment(8)
In which way, using NotNull() is an improvement?Courbet
by getting extension method to play with actual condition I can control what I define as Null hence there is an improvement. ( I normally ignore ! from my programming practice ).Aftergrowth
you might want to look into the Null Object PatternBorough
at least if(myObject != null) is a clear code. I'm not sure that using a pattern to avoid this kind of code will help the next programmer who will take your code.Courbet
Thank you @Jonesy I'd looked at it; creating dummy object makes no sense to me when compiler should be smart to make this judgement on. Anyways, this question arises when I m using Linq method calls in a chain and one of it returns null. Just like ?? C# team should be able to come with some creative idea. Just a thought.Aftergrowth
programmers.stackexchange.com/questions/12777/…Coprolite
The compiler isn't going to be able to make judgement calls on variable runtime valuesBorough
@Aftergrowth So it sounds like you would rather just get a null back at the end, with no indication what part of the chain failed, than get an exception with adequate information. That seems likely to confuse everyone else.Misguide
C
17

In the upcoming C# 6 (vNext) has the ?. operator (Null Conditional Operator) which easily allow you to chain null reference checks for every nested property.

An example of this would be:

int? first = customers?.[0].Orders?.Count();

This was a requested feature in the Visual Studio UserVoice site

Add ?. Operator to C#

You can see the status for all the new language features for C# 6.0 on the Codeplex site for Roslyn:

C# 6 Language Features Status

Crustacean answered 1/9, 2014 at 0:10 Comment(2)
pretty stoked for the new features coming outBorough
Ok it make sense now that currently there is no way so I am accepting this as my answer because this is exactly what I was looking for. I did heard about ?. but did not gave much attention to it. Thank you ScottAftergrowth
S
4

You can currently write these kinds of extension methods:

public static class Extensions
{
    public static R IfNotNull<T, R>(this T @this, Func<T, R> @select) where T : class
    {
        return @this.IfNotNull(@select, () => default(R));
    }

    public static R IfNotNull<T, R>(this T @this, Func<T, R> @select, Func<R> @default) where T : class
    {
        return @this != null ? @select(@this) : @default();
    }

    public static void IfNotNull<T>(this T @this, Action<T> @action) where T : class
    {
        if (@this != null)
        {
            @action(@this);
        }
    }
}

Then call them like so:

customObject 
    .IfNotNull(y => y.CallMe())
    .IfNotNull(y => y.CallMe2());
Suomi answered 1/9, 2014 at 0:56 Comment(0)
L
2

C# 5 has nothing to offer to simplify your code. You are stuck with either:

  • Nested if conditions
  • Complicated ternary expressions
  • Englobing try/catch catching NullReferenceException (you really shouldn't do that if a null in your chain is not really exceptionnal, i.e. it should not happen, but you want a keepsafe).

However, the future may be brighter, as the null-propagating operator ?. is a proposed feature of C# 6, and has already be implemented is Roslyn, so it would be a great surprise if it was not effectively in C# 6. With this operator, you will be able to write

customObject?.CallMe()?.CallMe2()?.[...] 

And the compiler will create the nested if for you (and even ensure that every method is only called once). So, A little more patience...

Lueck answered 1/9, 2014 at 0:15 Comment(7)
How on earth is this any better than an exception? It is basically a way of automatically eating exceptions. You are trying to just ignore the returned null and clip the execution chain, without addressing the logical error.Misguide
@JonJayObermark it is WAY better than eating an exception, because the new operator NEVER rises an exception. It's NOT a try/catch. It's evaluate the expression, check if the result is null, call the method/property/field if it's not and return null otherwise. There is no logical error in there, other than the one allowing null references in the language in the first place. This may be a design error (I do believe it is), but at least now we have a new tool to deal with this fault more easily.Lueck
OK, but then do you expect not to have to check if lists are empty, or if various other unexpected things are happending. Any null you have not chosen by design to make meaningful is really an exception whether or not you treat it as such.Misguide
@JonJayObermark The ?. operator will not by any mean give you the semantic of the null it may have. The meaning of the null is still depending on your business logic. I expect the most common use case will be var myValue = someVariable?.SomeProperty ?? defaultValue;Lueck
@JonJayObermark Whatever happens, it is NOT really an exception. An exception is something pretty grave. It interrupts the execution flow. It messes with the stack. It's very costly performance-wise. It will make debuggers break with the right configurations. It will produce log messages. All things you may want to avoid with a simple sanity check. The ?. operator does this sanity check, and not much more.Lueck
But it gives no feedback, so it is not a sanity check, it is not a check of any variety. Lack of "sanity" is the kind of thing that should not be tacitly assumed and passed along to the next poor fool. Getting an unexpected null is a design flaw, something relatively grave.Misguide
Also, automatically defaulting on null is pretty benign, but is still a design problem. If you are trying to call a method of something that may or may not be null, you are writing misleading code 95% of the time. The new notation just saves a few characters encouraging people to think less well.Misguide
M
1

Since people are tearing apart the answer that assumes you aren't an idiot, here is one that assumes you are.

You should have checked when customObject was created, because that is the point at which you would have know why you got a null. If you didn't check then, your best bet now is to let your code throw the exception. If there is still something you can do that makes perfect sense without having the object you clearly need, you can fix that up in the catch of the exception, but there probably isn't.

All of the mechanisms that ignore the significance of an unexpected null are just passing along the failure to have paid attention earlier, including habitual overuse of the proposed null-propagation operator, and all habits of checking objects before use. They are seeking an efficient response to a catastrophe that minimizes attention paid to it.

If you are signalling important information with the nullness of an object, that is probably a bad design choice, and you should have converted the null into more usable information when it was introduced.

Testing for the unexpected nullness of an object right before it is used, rather than right after it is produced cannot really help anyone. It is a code-smell that indicates you have misplaced responsibility, probably in one of these ways:

  1. You are unwilling to challenge people who make awkward design decisions without adequate explanation
  2. You do not feel you have control over something you have incorporated but are too lazy to wrap it
  3. You don't believe nulls should exist, and you are trying to ignore them, so you haven't thought through any policy on them.

The policy you indicate, of eating the null and continuing execution without analyzing it, is a bad one. If you intend to keep it, that is my other answer. You should instead do something reasonable.

In a lot of workplaces, putting an empty catch block anywhere is a clear documentation of future work. So it is doing something. But leaving it there is never an option that should stand in good code. Such a block would more reasonably convert the exception into a response that creates future work to fix the source of the error, even if the code-block implements a workaround that addresses the problem in a more local way.

Misguide answered 1/9, 2014 at 16:2 Comment(1)
This exactly. If your reference is null, and your code doesn't call the required method because of these misguided null checks, you will get a far worse and harder to diagnose failure later on.Grenadine
M
0

C# 6 does have the ? operator, otherwise you can look into monads, particularly the Maybe monad, e.g. here, here, elsewhere. Is it worth using monads in a non-functional language that wasn't designed with them in mind is a different question.

Maieutic answered 1/9, 2014 at 0:27 Comment(3)
IEnumerable, Nullable, Func, Lazy and a few other built-in types make me believe it IS worth it ;) In fact, most of the newer and most appealing langugage features of C# relate to monads or comonads (Task...)Lueck
@Lueck agree, but getting my head around monads as a concept wasn't easy since I'm not too familiar with functional languages and I wouldn't bother to just get around a few if (foo == null) blocks in this case.Maieutic
A very good thing about monads is that you don't really need to understand them to use them. And when you do understand them, it opens such a wide field of possibilities...Lueck
M
-6

This is the point of exception handling. If you want to respond to unusual circumstances like getting an unexpected null returned from a function, then catch the NullReferenceException, and call your recovery code from there. The exception itself will stop further calls, so if that is all you wish to accomplish, implement no recovery code. I do not suggest this is a reasonable response, something like this should generally at least be logged.

try
{
    customObject.CallMe().CallMe2()
}
catch (NullReferenceException ex)
{
   /* save enough details to determine why you got an unexpected null */
}

Do not use exception handling to accomplish normal programming tasks, but do not avoid it excessively, by checking for every possible contingency, no matter how rare. Decide what failures are likely in your code (that you can do anything about) and include a healthy list of catch'es at the end of each reasonable-sized block.

Misguide answered 1/9, 2014 at 0:4 Comment(16)
-1: absolutely not. NullReferenceException almost always represents a bug in your code. It should never be "handled" - it should be fixed.Galactic
Most exceptions represent thing that should be fixed. But fixing them immediately during a run is not practical. So exceptions exists so they can be logged and worked around. Like I said, don't use them on purpose to control flow, but if the mechanism had no use, it would not exist. Checking for null all the time is not a better solution, as the null return is just as likely to be a bug.Misguide
You're totally wrong. The mechanism doesn't exist for you to ignore exceptions. It exists so that you are informed of the bug in your code and can fix it ASAP. Or are you of the opinion that programs must never crash?Galactic
Any answer to this question will meet exactly this objection. Your code should not have bugs, so you should not have unexpected nulls. Ever. And the question has no general answer, it must be addressed casewise: You should return nulls when they are meaningful and check for them when they are likely.Misguide
The best way of handling unexpected nulls is to let the program die. But that is not an answer that fits the parameters of the question asked.Misguide
I have seen very few cases of NullReferenceException which are actually exceptional. Most are due to developers who do not know what null means, so don't check for it, or who are unaware of the possibility that null might be returned. In these cases, the best thing is usually for the program to crash immediately, informing their supervisors of this fact.Galactic
I am not a proponent of catching exceptions. And especially not of eating them. But if you are going to accomplish exactly what he asked, this is the least offensive way to do so.Misguide
Didn't I just say that? "The best way of handling unexpected nulls is to let the program die. But that is not an answer that fits the parameters of the question asked."Misguide
regardless there is no need to downgrade answers guys. Jon did his bit which you may be disagreed with.Aftergrowth
That is kind of the purpose of a downvote. But the basis of these objections is that no one should want an answer to this question. I sort of agree, but not as fanatically as seems to be popular.Misguide
@Aftergrowth That is exactly why this site provides a "downvote" option - for when you don't feel like the answer is correct. I too disagree with this answer - there are so many other factors involved. A simple null check is far and away more performant than allowing an exception to be thrown and the stack to be walked. NullReferenceExceptions are always fixable without catching them - I would reject any code I reviewed that expected a NullReferenceException and attempted to handle it in a catch.Mannose
@JonJayObermark: you seem to be making the assumption that the OP wanted an answer for a legitimate reason. I see no reason to believe that is the case. Without more detail, I will assume that the OP is like the vast majority of junior-to-intermediate developers I've met who do not understand null, or NullReferenceException. People like that need to be taught first what null is all about. Afterwards, they probably won't need to be taught how to "avoid" NullReferenceException.Galactic
I think it is hilarious that I am getting downvoted like mad and netting a positive gain int 'reputation' here. So there are people who disagree with this fanaticism, but they aren't saying anything.Misguide
@SimonWhitehead Right, the point of the exception is to be thrown very seldom. But in my experience, checks for nulls that do not end up throwing exceptions just hide problems and leave them for others. Either the null value is part of the design, in which case you do not just want to suppress continuation, you want to do something specific and important that was signaled by that choice. Or it is a surprise, and therefore really should be an exception. Bascially, "if you are cheating me, admit it, don't wrap it in an apology and still not do your job."Misguide
@JonJayObermark Of course. Very often there are things like nullable fields in a database that are valid values. They aren't "exceptional" - so there is absolutely no reason to throw an exception. This isn't hiding the problem for other developers - they should understand the domain well enough to know that NULL is a valid value. Also, allowing a NullRef to be thrown adds cognitive overhead to the code. When reviewing it, I will have to jump potentially tens of lines down to understand "normal" logic flow - that's just bad code.Mannose
@SimonWhitehead This person is talking about an object he is about to call a method on. That is very, very seldom an expected null. People are overgeneralizing out of the question. There is a big difference between data-members that are null, and active objects that you have just discovered are null, especially in a call chain.Misguide

© 2022 - 2024 — McMap. All rights reserved.