I am working through a training class for ASP.NET Core 6 and in it, in a class declaration, it has:
public class Pie
{
public Category Category { get; set; } = default!;
}
If I understand the default!
correctly (I'm experienced with C# 3 so this is new to me) it is assigning a default value to this property when the object is created. And as this property is a class, it is assigning null. And the !
is telling the compiler that it's ok to assign a null to this non nullable property.
Is that correct? If so, this does not appear to be necessary as the code will compile without this assignment and with no assignment, that property is null. So why is this done?
Also, in my programming to date it was assumed that a property in an object, if nothing is assigned to it, is null. And that any class member that is not a simple type is by definition nullable. Is that no longer true?
CodePudding user response:
Is that correct?
Yes, your understanding is correct. Assuming Category
is a reference type, default
is the same as null
here. If you assign null
to a non-nullable reference type, you will get a warning (CS8625), so !
is used to silence it.
If so, this does not appear to be necessary as the code will compile without this assignment and with no assignment, that property is null. So why is this done?
Without the assignment, you will get another warning (CS8618). While your code still compiles, it is a very bad idea to just leave the property uninitialised. This is because later on when you access the property, you see that it's a non-nullable type, so you don't do any null-checks, but it could turn out that it can still be null because it hasn't been initialised.
Also, in my programming to date it was assumed that a property in an object, if nothing is assigned to it, is null. And that any class member that is not a simple type is by definition nullable. Is that no longer true?
Only reference types have the default value of null, and any reference type is nullable, both of which I think is true since C# 1. You can still assign null to your Category
property, at your own risk. It's only a warning that will be emitted.
CodePudding user response:
Note, this goes off the assumption that Category
is a class, which seeing you tagged this "nullable reference types", that's a pretty good assumption.
Whether you include = default!
or not has no effect on the final compiled code.
public class C {
public string A { get; set; }
public string B { get; set; } = default!;
}
A
and B
will be treated the same way by the compiler.
public class C
{
[CompilerGenerated]
private string <A>k__BackingField;
[CompilerGenerated]
private string <B>k__BackingField;
public string A
{
[CompilerGenerated]
get { return <A>k__BackingField; }
[CompilerGenerated]
set { <A>k__BackingField = value; }
}
public string B
{
[CompilerGenerated]
get { return <B>k__BackingField; }
[CompilerGenerated]
set { <B>k__BackingField = value; }
}
}
And this makes sense. default
of a class is null
, so = default
just makes the property (and backing field) null, which is what would have been done anyways had you not typed = default
.
So why the !
? It's to make the compiler not throw a warning.
No really, that's it. It's to suppress the compiler warning you would have got had it not been there. When you use nullable reference types, the compiler wants you to always assign a value to your properties before the constructor exits. Using !
calms the compiler down as you pinky-promise that it won't be null when you try to read it back, but satisfies the requirement that some value is assigned.
public string A { get; set; }
// "A" might have a null value after the constructor exits
public string A { get; set; } = default;
// "Uh dude, you're trying to assign null to something that doesn't accept nulls".
public string A { get; set; } = default!;
// "Ok, you're assigning null, but I'm going to trust you know what you're doing".
You can also get around the problem by specifying a value in the constructor
public class C {
public string A { get; set; } // no warning
public C()
{
A = "f";
}
}
Another thing to note, C# 11 just came out (November 2022), which has the required
keyword, which takes care of all this nonsense.
public class C
{
public required string A { get; set; }
// no warning, no default, no constructor needed
}