About me

Michael L Perry

Improving Enterprises

Principal Consultant

@michaellperry

User login

The end of null reference exceptions

non-nullCorrespondence 2.0 is released! Please download the latest package from NuGet. Search for Correspondence and install the package appropriate for each project in your solution:

  • Correspondence.App – The main application (Windows 8, Windows Phone, WPF, or Silverlight)
  • Correspondence.Web.App – The main application (MVC)
  • Correspondence.Model – The model assembly (any client)
  • Correspondence.UnitTest – The unit test assembly (any client)

Or get the model and application in one project:

  • Correspondence.AllInOne – Windows 8, Windows Phone, WPF, or Silverlight)
  • Correspondence.Web.AllInOne – MVC

Null objects

Version 2.0 introduces null objects. Instead of returning null, the Correspondence API will now return an instance with the IsNull property set to true. You no longer have to write code like this:

if (_sessionPlace.Place == null ||
    _sessionPlace.Place.Room == null ||
    String.IsNullOrEmpty(_sessionPlace.Place.Room.RoomNumber.Value))
    return null;

return "Room: " + _sessionPlace.Place.Room.RoomNumber.Value;

Now you can eliminate all of those null checks. You will not get a null reference exception!

Object references from null objects will be other null objects. So you can string references together without worry. Native properties of null objects will be null or zero. So you can rewrite the above code as:

string roomNumber = _sessionPlace.Place.Room.RoomNumber.Value;
return String.IsNullOrEmpty(roomNumber)
    ? null
    : "Room: " + roomNumber;

API changes

I labeled this version 2.0 because it is technically a breaking change. If your code checks for null, that check will no longer be valid. Instead, you should check for one of the following new properties:

  • IsNull – Returns true if an optional predecessor is null, or a reference property is not set.
  • IsLoaded – Returns true if a predecessor or reference property has finished loading.

All references are loaded asynchronously, so IsLoaded will initially be false. When it finishes loading, it will become true and your view model will reevaluate the expression. You can just write your view models normally, and you will get the benefit of asynchronous loading.

Outside of a view model getter, you may need to ensure that a predecessor or property is loaded before continuing. You can use these methods to do just that:

  • Ensure() – Blocks until the fact is loaded. Returns the loaded fact. (Windows Phone, WPF, or Silverlight)
  • EnsureAsync() – Await to get the loaded fact. (Windows 8)

Finally, the FindFact method gets an upgrade. Not only does it return a null object if the fact is not found, it also has become observable. You can call FindFact in a view model. At first, it will return an IsLoaded = false object. Then, if the fact has not yet been created, it will change to an IsNull = false object. Finally, when the matching fact is added, it will return that fact. For each of these changes, your view model will notify property changed. The upshot is that you can write view models that depend upon facts that have not yet been created.

Slot slot = _community.FindFact(new Slot(_attendee, _sessionPlace.Place.PlaceTime));
if (slot.CurrentSchedules.Any())
    return "Scheduled";
else
    return "Not scheduled";

At first there is no Slot fact for the sessions time slot. So FindFact returns a null object, and CurrentSchedules will be empty. When the user schedules the session, the app will create both the Slot fact and the subsequent Schedule fact. That will cause the view model property to change, FindFact will return the actual Slot, and CurrentSchedules will contain the new scheduled session.

The FindFactAsync method is deprecated. Use FindFact().EnsureAsync() instead.

Please try out the new null object support in Correspondence 2.0. You will need to update your code to remove null checks and replace them with the IsNull property where necessary. But for the most part, you can just chain references together, and you will get the desired result. And you will not get a null reference exception!