So this is the meat of the question: Can Foo.Bar ever return null? To clarify, can \'_bar\' be set to null after it\'s evaluated as non-null and before it\'s value is retur
No, this is not thread safe.
The IL for the above compiles to:
.method public hidebysig specialname instance object get_Bar() cil managed
{
.maxstack 2
.locals init (
[0] object CS$1$0000)
L_0000: nop
L_0001: ldarg.0
L_0002: ldfld object ConsoleApplication1.Program/MainClass::_bar
L_0007: dup
L_0008: brtrue.s L_0010
L_000a: pop
L_000b: newobj instance void [mscorlib]System.Object::.ctor()
L_0010: stloc.0
L_0011: br.s L_0013
L_0013: ldloc.0
L_0014: ret
}
This effectively does a load of the _bar field, then checks its existence, and jumps ot the end. There is no synchronization in place, and since this is multiple IL instructions, it's possible for a secondary thread to cause a race condition - causing the returned object to differ from the one set.
It's much better to handle lazy instantiation via Lazy_bar is set), but I suspect that's a bug, and not the intended behavior.
In addition, Lazy makes setting difficult.
To duplicate the above behavior in a thread-safe manner would require explicit synchronization.
As to your update:
The getter for the Bar property could never return null.
Looking at the IL above, it _bar (via ldfld), then does a check to see if that object is not null using brtrue.s. If the object is not null, it jumps, copies the value of _bar from the execution stack to a local via stloc.0, and returns - returning _bar with a real value.
If _bar was unset, then it will pop it off the execution stack, and create a new object, which then gets stored and returned.
Either case prevents a null value from being returned. However, again, I wouldn't consider this thread-safe in general, since it's possible that a call to set happening at the same time as a call to get can cause different objects to be returned, and it's a race condition as which object instance gets returned (the set value, or a new object).