C# 8 - Null coalescing/compound assignment
There are a lot of cool new features in C# 8 and one of my favorites is the new Null coalescing assignment operator.
Hi friends.
There are a lot of cool new features in C# 8 and one of my favorites is the new Null coalescing assignment (or compound assignment, whichever you prefer) operator.
If you have ever written code like this:
private string _someValue;
public string SomeMethod()
{
// Let's do an old-school null check and initialize if needed.
if(_someValue == null)
{
_someValue = InitializeMyValue();
}
return _someValue;
}
private string InitializeMyValue()
{
// Do some expensive initialization that we only want to do once...
}
You can simplify this code A LOT!
private string _someValue;
public string SomeMethod()
{
// Using the awesome new ??= operator to do all in one go!
_someValue ??= InitializeMyValue();
return _someValue;
}
private string InitializeMyValue()
{
// Do some expensive initialization that we only want to do once...
}
That is the new ??=
operator in action, which takes care of doing the null check and assignment for you in one sweet syntactic sugar-rush! I also find it more readable, but some people might disagree, and that fine. Just pick whatever you prefer :)
And the best part is that it actually compiles down to the same efficient code! Just take a look at this sharplab.io sample to see what I mean.
You might have solved this with a Lazy<string>
or by using something like _someValue = _someValue ?? InitializeMyValue();
as well but that's still more code to write than the new operator and less efficient as well. The Lazy<T>
approach has some overhead and the null-coalescing operator + assignment has the added inefficiency of always making the variable assignment even if there is no need to (you can take a closer look at the ASM part of the SharpLab example above) but there it is for reference:
NullVsCoalescingVsCompound.NullCheckAndAssign()
L0000: cmp dword [ecx+0x4], 0x0
L0004: jnz L0014
L0006: mov eax, [0xeb52038]
L000c: lea edx, [ecx+0x4]
L000f: call 0x6fe391d0
L0014: mov eax, [ecx+0x4]
L0017: ret
NullVsCoalescingVsCompound.CoalescingOperatorAndAssign()
L0000: mov eax, [ecx+0x4]
L0003: test eax, eax
L0005: jnz L000d
L0007: mov eax, [0xeb52038]
L000d: lea edx, [ecx+0x4]
L0010: call 0x6fe391d0
L0015: mov eax, [ecx+0x4]
L0018: ret
NullVsCoalescingVsCompound.CompoundAssignment()
L0000: cmp dword [ecx+0x4], 0x0
L0004: jnz L0014
L0006: mov eax, [0xeb52038]
L000c: lea edx, [ecx+0x4]
L000f: call 0x6fe391d0
L0014: mov eax, [ecx+0x4]
L0017: ret
NullVsCoalescingVsCompound.LazyInitializer()
L0000: push ebp
L0001: mov ebp, esp
L0003: mov ecx, [ecx+0x8]
L0006: cmp dword [ecx+0x4], 0x0
L000a: jz L0013
L000c: call System.Lazy`1[[System.__Canon, System.Private.CoreLib]].CreateValue()
L0011: jmp L0016
L0013: mov eax, [ecx+0xc]
L0016: pop ebp
L0017: ret
NullVsCoalescingVsCompound.GetValue()
L0000: mov eax, [0xeb52038]
L0006: ret
Hope this helps! :)