According to the documentation:
The postfix ! operator has no runtime effect - it evaluates to the result of the underlying expression. Its only role is to change the null state of the expression, and to limit warnings given on its use.
Example:
IEnumerable<object?>? foo = GetFromSomewhere();
IEnumerable<object> bar = foo; // warning CS8619: Nullability of reference types in value of type 'IEnumerable<object?>' doesn't match target type 'IEnumerable<object>'.
(Notice that the warning doesn't correctly identify the value's type as IEnumerable<object?>?
, but claims it is IEnumerable<object?>
.)
When adding !
:
IEnumerable<object?>? foo = GetFromSomewhere();
IEnumerable<object> bar = foo!; // No warning.
!
seems to change the nullability of the concrete generic argument (from object?
to object
) too, not only the null state of the actual object instance denoted by the expression foo
.
But only if I annotate the type of bar
explicitly. When using var
instead, the behavior is more like how I would interpret the documentation:
IEnumerable<object?>? foo = GetFromSomewhere();
var bar = foo!;
IEnumerable<object> baz = bar; // warning CS8619: Nullability of reference types in value of type 'IEnumerable<object?>' doesn't match target type 'IEnumerable<object>'.
So bar
is inferred to be IEnumerable<object?>
, removing only the outermost question mark.
What are the exact semantics of the C# 8.0 null-forgiving operator (!
)?