I think the biggest reason would be as a good programming practice you should avoid assigning a variable to itself unnecessarily.
x = x;
Seems useless doesn't it? Instead of foo = foo ?? x; you should be doing
if(foo==null)
{
foo = x;
}
Not only this describes your intent better, it also avoids redundant writes to possibly a memory location, improving CPU cache utilization and register optimizations.
If it's not the performance but less typing that you're interested in, create a function:
static void SetIfNull(ref object variable, object value)
{
if(variable == null)
{
variable = value;
}
}
SetIfNull(ref foo, x);
JIT actually might be able to inline this, making your code as fast as a theoretical foo ??= x; I actually verified that JIT inlines such calls:
mov rax,qword ptr [rsi]
test rax,rax
jne 0000000000000093
mov qword ptr [rsi],rdi
However this still may not be a good programming practice as another programmer can assume the passed expression will not be executed if the variable is not null. Such as:
SetIfNull(ref foo, new List<x>());
In this case you introduce the overhead of creating object unnecessary. That's why I think my first solution
if(foo==null) foo = x;
fits to this purpose much better as it avoids second expression completely and has all small performance benefits.