Null propagation C# with Example
The ?. operator and ?[...] operator are called the null-conditional operator. It is also sometimes referred to by other names such as the safe navigation operator. This is useful, because if the . (member accessor) operator is applied to an expression that evaluates to null, the program will throw a NullReferenceException. If the developer instead uses the ?. (null-conditional) operator, the expression will evaluate to null instead of throwing an exception. Note that if the ?. operator is used and the expression is non-null, ?. and . are equivalent. Basics var teacherName = classroom.GetTeacher().Name; // throws NullReferenceException if GetTeacher() returns null View Demo If the classroom does not have a teacher, GetTeacher() may return null. When it is null and the Name property is accessed, a NullReferenceException will be thrown. If we modify this statement to use the ?. syntax, the result of the entire expression will be null: var teacherName = classroom.GetTeacher()?.Name; // teacherName is null if GetTeacher() returns null View Demo Subsequently, if classroom could also be null, we could also write this statement as: var teacherName = classroom?.GetTeacher()?.Name; // teacherName is null if GetTeacher() returns null OR classroom is null View Demo This is an example of short-circuiting: When any conditional access operation using the null-conditional operator evaluates to null, the entire expression evaluates to null immediately, without processing the rest of the chain. When the terminal member of an expression containing the null-conditional operator is of a value type, the expression evaluates to a Nullable of that type and so cannot be used as a direct replacement for the expression without ?.. bool hasCertification = classroom.GetTeacher().HasCertification; // compiles without error but may throw a NullReferenceException at runtime bool hasCertification = classroom?.GetTeacher()?.HasCertification; // compile time error: implicit conversion from bool? to bool not allowed bool? hasCertification = classroom?.GetTeacher()?.HasCertification; // works just fine, hasCertification will be null if any part of the chain is null bool hasCertification = classroom?.GetTeacher()?.HasCertification.GetValueOrDefault(); // must extract value from nullable to assign to a value type variable Use with the Null-Coalescing Operator (??) You can combine the null-conditional operator with the Null-coalescing Operator (??) to return a default value if the expression resolves to null. Using our example above: var teacherName = classroom?.GetTeacher()?.Name ?? "No Name"; // teacherName will be "No Name" when GetTeacher() // returns null OR classroom is null OR Name is null Use with Indexers The null-conditional operator can be used with indexers: var firstStudentName = classroom?.Students?[0]?.Name; In the above example: The first ?. ensures that classroom is not null. The second ? ensures that the entire Students collection is not null. 0 The third ?. after the indexer ensures that the [ ] indexer did not return a null object. It should be noted that this operation can still throw an IndexOutOfRangeException. Use with void Functions Null-conditional operator can also be used with void functions. However in this case, the statement will not evaluate to null. It will just prevent a NullReferenceException. List list = null; list?.Add("hi"); // Does not evaluate to null Use with Event Invocation Assuming the following event definition: private event EventArgs OnCompleted; When invoking an event, traditionally, it is best practice to check if the event is null in case no subscribers are present: var handler = OnCompleted; if (handler != null) { handler(EventArgs.Empty); } Since the null-conditional operator has been introduced, the invocation can be reduced to a single line: OnCompleted?.Invoke(EventArgs.Empty); Limitations Null-conditional operator produces rvalue, not lvalue, that is, it cannot be used for property assignment, event subscription etc. For example, the following code will not work: // Error: The left-hand side of an assignment must be a variable, property or indexer Process.GetProcessById(1337)?.EnableRaisingEvents = true; // Error: The event can only appear on the left hand side of += or -= Process.GetProcessById(1337)?.Exited += OnProcessExited; Gotchas Note that: int? nameLength = person?.Name.Length; // safe if 'person' is null is not the same as: int? nameLength = (person?.Name).Length; // avoid this because the former corresponds to: int? nameLength = person != null ? (int?)person.Name.Length : null; and the latter corresponds to: int? nameLength = (person != null ? person.Name : null).Length; Despite ternary operator ?: is used here for explaining the difference between two cases, these operators are not equivalent. This can be easily demonstrated with the following example: void Main() { var foo = new Foo(); Console.WriteLine("Null propagation"); Console.WriteLine(foo.Bar?.Length); Console.WriteLine("Ternary"); Console.WriteLine(foo.Bar != null ? foo.Bar.Length : (int?)null); } class Foo { public string Bar { get { Console.WriteLine("I was read"); return string.Empty; } } } Which outputs: Null propagation I was read 0 Ternary I was read I was read 0 View Demo To avoid multiple invocations equivalent would be: var interimResult = foo.Bar; Console.WriteLine(interimResult != null ? interimResult.Length : (int?)null); And this difference somewhat explains why null propagation operator is not yet supported in expression trees.