Whilst the other answer is correct for the purposes of most "normal" usage of a type, there are at least two slightly more unusual scenarios where _bar could be null in Dispose() despite the appearing to always be initialized via the sole constructor.
It's worth understanding that the creation of an object in .NET is essentially a 2 step process:
- Allocate the object
- Call the appropriate constructor
There are a number of ways that step 1 can occur without step 2, which leaves you with an object for which all the fields are in the default state for the respective type (e.g. null for reference types, zero for numeric types, etc.).
The first is intentionally via FormatterServices.GetUninitializedObject(), which is primarily used by code intended to deserialize an object (e.g. after having been persisted to a file). As the name perhaps suggests, it allocates an instance of an object (step 1), but does not execute the constructor. For your example Foo class, this would result in _bar remaining null. If Dispose() were then to be called on the instance without having had _bar initialized by some other means (e.g. reflection), then a null reference exception would be thrown in the Dispose() method without the null check on _bar.
The other even less common possibility results from a long-standing (almost 5 year old) bug in .NET itself. Whilst it won't occur on the exact code in your example, a slight modification (the introduction of a Finalizer) makes it possible.
Here's an example:
class Foo : IDisposable
{
private readonly Bar _bar;
// The constructor needs to have at least one argument
public Foo(string someArg)
{
_bar = new Bar();
}
public void Dispose()
{
Dispose(true);
}
// And we need a finalizer
~Foo()
{
Dispose(false);
}
private void Dispose(bool disposing)
{
Console.WriteLine("{0} and _bar is {1}", disposing ? "Disposing" : "Finalizing", _bar == null ? "null" : "not null");
}
}
And to trigger it:
static void Main(string[] args)
{
try
{
// We intentionally trigger an exception when getting the arg to pass to `Foo()`
// to trigger the bug, however in real-life example you might call a method
// here that sometimes throws
using (var foo = new Foo(args[-1]))
{
// ...
}
}
catch
{
}
// These two lines aren't required for the bug to be triggered,
// they simply allow us to see it without waiting for a GC.
GC.Collect();
GC.WaitForPendingFinalizers();
}
The output from this when I run it is:
Finalizing and _bar is null
The explanation is relatively straightforward and again results from the two step process of creating an object. One might expect that .NET would always perform step #2 immediately after step #1, but in fact it places the code that determines the values of the constructor arguments between the two steps, if any of that code throws an exception as we (intentionally) do here, then step #2 is skipped. Whilst we can't access the object that was allocated (because it never gets assigned to the foo variable), because it's finalizable, .NET still keeps track of it in order to call its finalizer later.
Whilst both of these might seem somewhat hypothetical examples, I have encountered both of them in production code bases over the years, so worth being aware of!