In C# 6.0, the new syntax let us write read-only auto-properties with using an initializer:
public bool AllowsDuplicates { get; } = true;
Likewise, we can write it using an expression body getter:
public bool AllowsDuplicates => true;
For simple types, these two should have the same effect: a read-only auto-property that returns true.
But is one of them preferred over the other? I suspect that the former uses a backing field:
private readonly bool _backingField = true;
public bool AllowsDuplicates {
get {
return _backingField;
}
}
Whereas the latter is turned into something like:
public bool AllowsDuplicates {
get {
return true;
}
}
Is that right, or is the compiler smarter than this?
I suspect that the former uses a backing field
The auto-property initializer does in fact creating a backing field! You can throw this in ILSpy and see it in the output:
public class One
{
public bool AllowsDuplicates
{
[CompilerGenerated]
get
{
return this.<AllowsDuplicates>k__BackingField;
}
}
public One()
{
this.<AllowsDuplicates>k__BackingField = true;
base..ctor();
}
}
public class Two
{
public bool AllowsDuplicates
{
get
{
return true;
}
}
}
But is one of them preferred over the other?
For the specific example in the question, the auto-property would allow a constructor to ask for a bool
and assign it. The second style would not. If the intent is to use it as a "default value" which can be modified once during construction, then an auto-property is the right choice.
class Test
{
// Could assign this in the second constructor
public bool AllowsDuplicates { get; } = true;
// Cannot assign this in the second constructor
public bool AllowsDuplicates => true;
public Test()
{
// Default value used
}
public Test(bool value)
{
AllowsDuplicates = value;
}
}
I've seen expression bodied syntax win out most when it's a cover for a small function that is utilized as a property. A struct in Eric Lippert's dedoublifier has a nice example of this:
public DoubleHelper(double d)
{
this.RawBits = (ulong)BitConverter.DoubleToInt64Bits(d);
}
public ulong RawBits { get; }
// RawSign is 1 if zero or negative, 0 if zero or positive
public int RawSign => (int)(RawBits >> 63);
public int RawExponent => (int)(RawBits >> 52) & 0x7FF;
public long RawMantissa => (long)(RawBits & 0x000FFFFFFFFFFFFF);
public bool IsNaN => RawExponent == 0x7ff && RawMantissa != 0;
public bool IsInfinity => RawExponent == 0x7ff && RawMantissa == 0;
public bool IsZero => RawExponent == 0 && RawMantissa == 0;
public bool IsDenormal => RawExponent == 0 && RawMantissa != 0;
There's one value which is assigned in the constructor, and the rest are property values which are computed based off of it.