Автоматическое свойство только для чтения для простых типов: инициализатор против Геттера тела выражения


В C# 6.0 новый синтаксис позволяет записывать авто-свойства только для чтения с использованием инициализатора:

public bool AllowsDuplicates { get; } = true;

Аналогично, мы можем записать его, используя геттер тела выражения:

public bool AllowsDuplicates => true;

Для простых типов эти два должны иметь одинаковый эффект: автоматическое свойство только для чтения, которое возвращает true.

Но разве один из них предпочтительнее другого? Я подозреваю, что первый использует резервное поле:
private readonly bool _backingField = true;
public bool AllowsDuplicates {
    get {
        return _backingField;
    }
}

Тогда как последнее превращается в нечто вроде:

public bool AllowsDuplicates {
    get {
        return true;
    }
}

Это что правильно, или компилятор умнее этого?

2 4

2 ответа:

Я подозреваю, что первый использует резервное поле

Инициализатор автоматического свойства фактически создает резервное поле! Вы можете бросить это в ILSpy и увидеть его в выходных данных:

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;
        }
    }
}
Но разве один из них предпочтительнее другого?

Для конкретного примера в вопросе свойство auto позволит конструктору запросить bool и назначить его. Второй стиль-нет. Если намерение состоит в том, чтобы использовать его как " дефолт значение", которое может быть изменено один раз во время строительства, то авто-свойство является правильным выбором.

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;
    }
}

Я видел, что синтаксис с плотным выражением выигрывает больше всего, когда он служит прикрытием для небольшой функции, которая используется в качестве свойства. Структура в дедублификаторе Эрика Липперта имеет хороший пример этого:

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;

Есть одно значение, которое назначается в конструкторе, а остальные значения свойств, которые вычисляются на основе него.

Я подозреваю, что первый использует резервное поле, в то время как второй является превратился во что-то вроде, верно?

Да, как вы и сказали, авто-инициализатор свойств устанавливает значение резервного поля в момент объявления.

И тело выражения является упрощением синтаксиса для тела get.

Но разве один из них предпочтительнее другого?

Зависит от того, является ли ваше свойство более сложным что просто возвращая то же самое значение, например, как свойство Elapsed или Current, или что-нибудь, что должно быть вычислено выражение-bodied является более подходящим.
В выражении -bodied вы определяете код, который будет выполняться при каждом обращении к свойству.

Если предполагается, что ваше свойство является неизменяемым, то предпочтительным будет только начальное постоянное значение авто-инициализатор свойств.