Frequently in applications we encounter situations that could throw a NullReferenceException
; for example, assuming the following method's argument is a user defined reference type, accessing the reference without first checking if it is null, can result in a NullReferenceException
:
public void ProcessObject(MyObject obj) { ... }
The common way to ensure that these exceptions don't occur is to check the reference prior to accessing it:
public void ProcessObject(MyObject obj) {
if (obj != null) {
...
}
}
This is a relatively straight forward process, and allows us to perform some kind of logging (if we desire) if the reference is null. However, let's say that MyObject
contains sub objects that we must utilize:
public sealed class MyA { public int X { get; set; } }
public sealed class MyB { ... }
public sealed class MyC { ... }
public sealed class MyD { ... }
public sealed class MyObject {
public MyA A { get; set; }
public MyB B { get; set; }
...
}
Ordinarily, I'd side with the idea that the handling in this situation is the exact same:
if (obj?.A != null)
...
However, let's assume that MyObject
is a rather large data model, defined by an external source. Let's also assume that I need to access many of these sub-objects in a way that makes it incredibly tedious to check each individually:
if (obj?.A != null) {
values.Add(obj.A.X)
values.Add(obj.A.Y)
}
if (obj?.B != null)
values.Add(obj.B.Z)
...
Instead, perhaps it's more convenient to create a last accessed object, assuming that if something fails there's no need to continue:
dynamic lastAccessedObject { Name = nameof(obj), Value = JsonConvert.SerializeObject(obj) };
try {
values.Add(obj.A.X);
values.Add(obj.A.Y);
values.Add(obj.B.Z);
} catch (Exception e) when (e is NullReferenceException) {
LogMessage($"An attempt was made to process null data; the last accessed object is: {JsonConvert.SerializeObject(lastAccessedObject)}");
}
This is a very simple example, but I believe it would be useful in a scenario where there are multiple top level objects and many sub-objects are utilized during the process. If the top level object isn't null, and we encounter an exception with one of its sub-objects, then the JSON data would reflect the sub-object as null. As such, from a readability point of view, I believe this method to be more concise, and as such, more legible than a method with many nested logical evaluations checking for null and logging what was null.
With that in mind, I'd like a second opinion on this idea. To prevent an X/Y problem, the goal is to simply record what object causes a null reference exception to be thrown, since the call stack doesn't always give us this information.
Will recording the last accessed object potentially confuse the purpose of the method? Are there any gotchas from a performance or runtime perspective that should be considered? What about from a peer review perspective? Does this method cause you to stop and think too much compared to the typical logical evaluations? Does it make sense to do this only if the situation dictates it, or not at all? These are just a few of my questions; feel free to answer them, but my primary question is simply:
Is recording the top level object as JSON data less legible than the typical process of logical evaluations?
null
's. That said, this may be a larger problem with other data-validation, so a more general tactic might be appropriate.IValidatableObject
(downside: anytime a design requires every type to implement an interface, the end result could be massive pollution. But for data objects, the benefits may justify this cost.)