- Clean Code in C#
- Jason Alls
- 698字
- 2025-04-04 12:56:15
Avoiding NullPointerExceptions
NullReferenceException is a common exception that has been experienced by most programmers. It is thrown when an attempt is made to access a property or method on a null object.
To defend against computer program crashes, the common course of action among fellow programmers is to use try{...}catch (NullReferenceExceptionre){...} blocks. This is a part of defensive programming. But the problem is that, a lot of the time, the error is simply logged and rethrown. Besides this, a lot of wasted computations are performed that could have been avoided.
A much better way of handling ArgumentNullExceptions is to implement ArgumentNullValidator. The parameters of a method are usually the source of a null object. It makes sense to test the parameters of a method before they are used and, if they are found to be invalid for any reason, to throw an appropriate Exception. In the case of ArgumentNullValidator, you would place this validator at the top of the method and then test each parameter. If any parameter was found to be null, then NullReferenceException would be thrown. This would save computations and remove the need to wrap your method's code in a try...catch block.
To make things clear, we will write ArgumentNullValidator and use it in a method to test the method's arguments:
public class Person
{
public string Name { get; }
public Person(string name)
{
Name = name;
}
}
In the preceding code, we have created the Person class with a single read-only property called Name. This will be the object that we will use to pass into the example methods to cause NullReferenceException. Next, we will create our Attribute for the validator called ValidatedNotNullAttribibute:
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
internal sealed class ValidatedNotNullAttribute : Attribute { }
Now that we have our Attribute, it's time to write the validator:
internal static class ArgumentNullValidator
{
public static void NotNull(string name,
[ValidatedNotNull] object value)
{
if (value == null)
{
throw new ArgumentNullException(name);
}
}
}
ArgumentNullValidator takes two arguments:
- The name of the object
- The object itself
The object is checked to see if it is null. If it is null, ArgumentNullException is thrown, passing in the name of the object.
The following method is our try/catch example method. Notice that we log a message and throw the exception. However, we don't use the declared exception parameter, and so by rights, this should be removed. You will see this quite often in code. It is unnecessary and should be removed to tidy the code up:
private void TryCatchExample(Person person)
{
try
{
Console.WriteLine($"Person's Name: {person.Name}");
}
catch (NullReferenceException nre)
{
Console.WriteLine("Error: The person argument cannot be null.");
throw;
}
}
Next, we will write our example method that will use ArgumentNullValidator. We will call it ArgumentNullValidatorExample:
private void ArgumentNullValidatorExample(Person person)
{
ArgumentNullValidator.NotNull("Person", person);
Console.WriteLine($"Person's Name: {person.Name}");
Console.ReadKey();
}
Notice that we have gone from nine lines, including braces, to only two lines. We also don't attempt to use the value before it has been validated. All we need to do now is modify our Main method to run the methods. Test each method by commenting out one of the methods and running the program. When you do this, it is best to step through your code to see what's going on.
The following is the output of running the TryCatchExample method:

The following is the output of running ArgumentNullValidatorExample:

If you study the previous screenshots carefully, you will see that we have only logged the error once when using ArgumentNullValidatorExample. When throwing the exception using TryCatchExample, the exception is logged twice.
The first time, we have a meaningful message, but the second time, the message is cryptic. However, the exception that is logged by the calling method, Main, is not cryptic at all. It is, in fact, very helpful as it shows us that the value cannot be null for the Person parameter.
Hopefully, this section has shown you the value of checking your parameters in your constructors and methods before you use them. By doing this, you can see how argument validators reduce your code, thus making it more readable.
Now, we will look at implementing business rules for specific exceptions.