tags:

views:

923

answers:

17

At the top of my functions I'm trying the best way to handle a null coming into my procedures in C#. Which is the best way for checking and handling the null and why? I've added the complete code of what I'm using right now and Resharper is telling me to use Option #1. Normally I do what it says as I understand why it makes it more efficient. This time though I'm not sure so I must ask.

Option #1
if (sender == null) return;

// Code goes here

or

Option #2
if (sender != null)
{ 
     // Code goes here
}

Complete Code
        private void EmployeeMouseHoverToolTip(object sender, EventArgs e)
        {
            if (sender != null)
            {
                var sUserIdentifier = ((C1TextBox)sender).Text;
                var userIdentifier = Guid.Empty;
                if (Utilities.IsGuid(sUserIdentifier))
                {
                    userIdentifier = new Guid(sUserIdentifier);
                }

                var toolTipText = Utilities.UserIdentifierToName(userIdentifier);
                c1SuperTooltip.SetToolTip(sender as C1TextBox, toolTipText);
            }
        }
+39  A: 

The best code is to disallow null (instead of what you’re doing). This isn’t always possible (sometimes it’s important to handle null in a meaningful way) – but in most of the cases it is.

Then all you need to do (in defensive coding) is to add a null check and throw an exception:

if (arg == null)
    throw new ArgumentNullException("arg");

Many (if not most) methods in the .NET framework and in good libraries do it that way.

Apart from that, the sender of an event should never be null and I’d say that a check for it is redundant. If null gets passed to this event, there’s something seriously wrong with your code.

The way you handle null (by silently swallowing it and doing nothing) may mask serious bugs in the application and is rarely, if ever, appropriate. Errors in the code should raise suspicious behaviour, not be swept under the carpet.

Konrad Rudolph
Might want to be clear that this has very different behaviour to the two options in the question.
Daniel Earwicker
This was a good article on refactoring that I read yesterday where they did remove the null check and the showed the improvement it made in the code that could be written: http://www.lostechies.com/blogs/derickbailey/archive/2010/09/24/a-refactoring-explicit-modeling-and-reducing-duplication.aspx
Paul Hadfield
@Daniel: Good comment. Is it better now?
Konrad Rudolph
+1 Beeea-utiful!
Daniel Earwicker
@Konrad: if you think that an argument is _never_ null, then testing and throwing is good practice (it's the same as assertions in non-C# C-like languages). It documents your expectation, lets you know if it ever really happens, and decent supporting tools can see the assertion and know what conditions to use when generating automated path exercises and other artefacts.
Graham Lee
@Graham: Not quite: Assertions are the same as assertions, not exceptions. Of course in general I agree with your statement but in some cases this is really redundant. Doing null-checks on event arguments in event handlers is not customary, to say the least. These methods should never get called manually anyway (so documenting expectations is simply not an issue: the client doesn’t raise exceptions, the library does), and if there really *is* a bug in the library that raises the event and fails to pass an argument, then this will be immediately evident by glancing at the stack trace.
Konrad Rudolph
@Konrad: I was under the impression that C# Assert was to do with code rights. My mistake.
Graham Lee
A: 

In your exact example here, go for the inverse of what you are doing, so if sender is null, go for an early exit. It reads better (IMO) and results in less nesting.

So Option #1 in this instance

Paul Hadfield
+3  A: 

I personally prefer the first option

if (sender == null) return;

It cuts down on nesting and increases readability.

Evil Andy
So the function returned.. And now? What about Exceptions / Error Handling?
Henrik P. Hessel
Henrik, maybe this isn't an error condition.
John Fisher
A: 

If you're not going to handle a null sender, then I would go with the first option (and make sure it's the first line in the handler so it doesn't get hidden by other code).

If you think you may handle a null sender eventually, I would go with the second option since it offers you a better way to handle things as well as maintaining a single return point for the handler.

Justin Niessner
A: 

The performance impact is minimal, so I wouldn't even worry about that. Option 1 is better because it is more readable and less cloudy...

More readable because the conditional is not negated, and there isn't an unnecessary scope block.

Nix
+9  A: 

Option 1 is being suggested by resharper, in my opinion, because it makes for easier to read code. You'll end up with:

  • Less indenting
  • Code that asserts its requirements and reacts to them right at the top of the method (if sender is null, I return, straight away)
  • Code that's generally easier to maintain because it's clearer

As far as performance is concerned, there's probably little difference (though if it matters to you, measure it). There's nothing to stop the JIT compiler from re-writing one form to the other anyway, if they don't get output as identical MSIL by the C# compiler anyway.

Rob
+1  A: 

I generally go with Option #1. I feel it's cleaner and it's meaning is more clear. Whoever's reading the code knows that if we've safely gotten past the null check and have exited then there's no chance of us messing around with a null value in sender later on.

Guy
A: 

Option #1 I guess would reduce Cyclomatic complexity. In option #2 , if there is another if conditions then it would come under the if clause and increase the complexity

Ramesh
+1  A: 

I prefer

if (sender == null) return;

with it there are less nested operations in code and early exit if there is null.

nihi_l_ist
+11  A: 

Why not just pretend that a null reference never occurs, and don't catch the NullPointerException?

You get a stack trace, plenty of information, and it's handled as an exception.

Arafangion
sometimes, such as if you're inserting the argument into a collection that can contain nulls, it won't raise an exception so you need to explicitly test for it, but for most cases I believe this is the best approach. +1
rmeador
A: 

Based on what ILDASM says, I would say option #2 is (slightly) more efficient:

Code:

class Program
{
    static void Main(string[] args)
    {
        Method1(null);
        Method2(null);
    }

    static void Method1(object sender)
    {
        if (sender == null)
            return;

        for (int x = 0; x < 100; x++)
        {
            Console.WriteLine(x.ToString());
        }
    }

    static void Method2(object sender)
    {
        if (sender != null)
        {
            for (int x = 0; x < 100; x++)
            {
                Console.WriteLine(x.ToString());
            }
        }
    }
}

ILDASM for Method1:

.method private hidebysig static void  Method1(object sender) cil managed
{
  // Code size       47 (0x2f)
  .maxstack  2
  .locals init ([0] int32 x,
           [1] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldnull
  IL_0003:  ceq
  IL_0005:  ldc.i4.0
  IL_0006:  ceq
  IL_0008:  stloc.1
  IL_0009:  ldloc.1
  IL_000a:  brtrue.s   IL_000e
  IL_000c:  br.s       IL_002e
  IL_000e:  ldc.i4.0
  IL_000f:  stloc.0
  IL_0010:  br.s       IL_0025
  IL_0012:  nop
  IL_0013:  ldloca.s   x
  IL_0015:  call       instance string [mscorlib]System.Int32::ToString()
  IL_001a:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001f:  nop
  IL_0020:  nop
  IL_0021:  ldloc.0
  IL_0022:  ldc.i4.1
  IL_0023:  add
  IL_0024:  stloc.0
  IL_0025:  ldloc.0
  IL_0026:  ldc.i4.s   100
  IL_0028:  clt
  IL_002a:  stloc.1
  IL_002b:  ldloc.1
  IL_002c:  brtrue.s   IL_0012
  IL_002e:  ret
} // end of method Program::Method1

ILDASM for Method2:

.method private hidebysig static void  Method2(object sender) cil managed
{
  // Code size       44 (0x2c)
  .maxstack  2
  .locals init ([0] int32 x,
           [1] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldnull
  IL_0003:  ceq
  IL_0005:  stloc.1
  IL_0006:  ldloc.1
  IL_0007:  brtrue.s   IL_002b
  IL_0009:  nop
  IL_000a:  ldc.i4.0
  IL_000b:  stloc.0
  IL_000c:  br.s       IL_0021
  IL_000e:  nop
  IL_000f:  ldloca.s   x
  IL_0011:  call       instance string [mscorlib]System.Int32::ToString()
  IL_0016:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001b:  nop
  IL_001c:  nop
  IL_001d:  ldloc.0
  IL_001e:  ldc.i4.1
  IL_001f:  add
  IL_0020:  stloc.0
  IL_0021:  ldloc.0
  IL_0022:  ldc.i4.s   100
  IL_0024:  clt
  IL_0026:  stloc.1
  IL_0027:  ldloc.1
  IL_0028:  brtrue.s   IL_000e
  IL_002a:  nop
  IL_002b:  ret
} // end of method Program::Method2
code4life
@code4life: Should we really code for compilers and computers and not for developers? I'm all in favor with small changes like `++i` instead of `i++` for example, but in my opinion code readability should be prioritized.
Patrick
You should mention the compiler and the options that you've used.
Cristian Ciupitu
@Patrick: sometimes, yes. It all depends on what the situation calls for: performance vs. readability, or vice-versa. We shouldn't be blindly dogmatic and emphasize one over the other - this eventually leads to "style over substance" type of architecture, which I personally will never adopt. Despite our differing viewpoints, I think what I've posted is useful information regardless what our coding approach may be.
code4life
I downvoted this because it is microoptimization, and that is almost always a sign of bad advice. In this case, the performance gain would be completely insignificant, and the only consideration really should be code readability.
AHM
@AHM: Fair enough, however, as I mentioned, I think the ILDASM reveals some valuable insights as to what our code builds into. Code readability should not be your only consideration, especially in projects that reach a large scale. By the way, I'm not advocating micro-optimization here, just **awareness**.
code4life
+5  A: 

This is an event handler, it should only be called by controls in response to an event (never directly by your own code), so you shouldn't care about null checks or even type checks on the sender parameter (if you only attach this event handler to the same type of control). I'd do it simply like this:

private void EmployeeMouseHoverToolTip(object sender, EventArgs e) {  
  var txtBox = (C1TextBox)sender;
  var sUserIdentifier = txtBox.Text;
  var userIdentifier = Utilities.IsGuid(sUserIdentifier) ? 
    new Guid(sUserIdentifier) : 
    Guid.Empty;
  var toolTipText = Utilities.UserIdentifierToName(userIdentifier);
  c1SuperTooltip.SetToolTip(txtBox, toolTipText);
}

Actually, I'd go one step further and separate the logic to get the tooltip text from the logic to read and update the UI. Something like this:

private void EmployeeMouseHoverToolTip(object sender, EventArgs e) {  
  var txtBox = (C1TextBox)sender;
  var toolTipText = ResolveUpdatedTooltipText(txtBox.Text);
  c1SuperTooltip.SetToolTip(txtBox, toolTipText);
}

private string ResolveUpdatedTooltipText(string sUserIdentifier) {
  var userIdentifier = ResolveGuid(sUserIdentifier);
  return Utilities.UserIdentifierToName(userIdentifier);
}

private Guid ResolveGuid(string sUserIdentifier) {
  return Utilities.IsGuid(sUserIdentifier) ? 
    new Guid(sUserIdentifier) : 
    Guid.Empty;
}

Therefore, you shouldn't use any of the options you provided.

Jordão
Uhm, I’m a bit embarrassed that my answer has so many votes. This here is really the better answer. +1
Konrad Rudolph
@Konrad: Oh, not really. I voted for yours too. It's important to also mention the right way to really validate arguments (normally, by throwing exceptions) and that ignoring error conditions might hide bugs, which you did superbly.
Jordão
Good answer - about not checking + splitting out. BUT ... I disagree with the statememnt _"This is an event handler, it should only be called by controls in response to an event (never directly by your own code), "_ - if you know what you're doing, there's nothing wrong with calling 'MyEvent(MySender,null)' to send it a n object to work with, or 'MyEvent(null,null)' if the event doesn't refer to sender at all. _you've got a little point, this is ugly._ Prettier way might be to call a common sub from both. But that's more code, which I hate! Esp. when the rest of my code is ugly anyway ;-)
FastAl
@FastAI: That's exactly why I refactored the code, to be able to reuse the logic without calling the event handler directly. It's more code, I agree, but it's better designed; you gain in reusability, understandability and maintainability. I tend to prefer many [small, focused methods](http://codebetter.com/blogs/jeremy.miller/archive/2006/12/03/Composed-Method-Pattern.aspx); than big, uncohesive ones.
Jordão
+5  A: 

Don't check for it.

If you get nulls, you've added the handler to something you shouldn't have. And if some other bug causes it, you should be handling it with WinForms' global exception handler so the program doesn't bomb, logging it, and uploading the logs to your site whichever way you can to check for such errors.

FastAl
If you handle every null that way, you'll eventually end up with code that will always crash. There are plenty of situations where nulls are perfectly acceptable input and should simply do nothing.
John Fisher
@John - 100% true! I have null checks all over the place. In fact I wish that somehow .net were 100% nullable object use only. Somehow. Have no clue what other ramifications that would have though and I am guessing MS has some clue.
FastAl
+1  A: 

Resharper likes option 1 as it is a pre-condition checker. When pre-conditions are not met, an early return is done.

Usually an early return is destructive for code-readability, but in this case it is very readable.

This way you can easily add extra pre-condition checks, like checking the contents of the EventArgs e, without having to rework the main function code.

Rob Vermeulen
A: 

Similar to @Konrad but I like the idea of adding the exception in the end if the code

if( sender != null )
{
    // Code goes here
    ....
} else
    throw new ArgumentNullExcpetion("sender");

So I vote for Option #2

jalexiou
this causes unneeded nesting and makes code harder to read.
Micah
So it is better to pre-check and throw the Exception in the begining if needed?
jalexiou
A: 

A variation of option #1 that either returns right away, or throws an exception. The trick is knowing which method to use. My rule of thumb is that if it's part of a public interface, then throw an exception. If it's something you have control over deep down in your framework then just return immediately and handle the null reference at that level.

public void IHaveNoControlOverWhereThisMethodIsCalled(object arg)
{
    if(arg == null)
        throw new ArgumentNullException("arg");    
}

private void TheOnlyCallersOfThisMethodComeFromMe(object arg)
{
    //I should do all my public parameter checking upstream and throw errors
    //at the public entry point only.
    if(arg == null)
         return;

}

In the specific case of your event handler:

private void EmployeeMouseHoverToolTip(object sender, EventArgs e)
{
    var txtSender = sender as C1TextBox;
    if(txtSender == null) return;

    var sUserIdentifier = txtSender.Text;
    var userIdentifier = Guid.Empty;
    if (Utilities.IsGuid(sUserIdentifier))
    {
        userIdentifier = new Guid(sUserIdentifier);
    }

    var toolTipText = Utilities.UserIdentifierToName(userIdentifier);
    c1SuperTooltip.SetToolTip(sender as C1TextBox, toolTipText);
}
Micah
A: 

I do not handle null for private methods, as I always make sure no null value will be sent to my private methods. If something went wrong and null has been passed to private method, so the exception will be thrown from as it should be and I will know that I did something wrong. If you always check for null value for private method, you might be skipping some logical error at run-time and you will never know you got one in your code until it hits you in the production.

tia