Авто-свойство только для чтения для простых типов: Initializer VS Expression Body Getter

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

Так ли это, или компилятор умнее этого?


person Mikkel R. Lund    schedule 02.05.2016    source источник
comment
Вопрос немного спорный, так как public const было бы более подходящим для этого случая. В остальном очень похоже на этот недавний вопрос   -  person Henk Holterman    schedule 02.05.2016
comment
@HenkHolterman Нет, не верю. Я специально спрашиваю о простых типах.   -  person Mikkel R. Lund    schedule 03.05.2016
comment
Но зачем вообще использовать свойства для таких значений?   -  person Henk Holterman    schedule 03.05.2016
comment
Потому что свойство определено в интерфейсе ;) И, кроме того, вы должны скорее выставлять свойство, чем поле, так как это позволит вам позже изменить его внутреннюю реализацию, не нарушая клиентский код.   -  person Mikkel R. Lund    schedule 03.05.2016
comment
Таким образом, есть (может быть) небольшая разница в используемом пространстве, которую вы никогда не заметите.   -  person Henk Holterman    schedule 03.05.2016


Ответы (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;
        }
    }
}

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

Для конкретного примера в вопросе автоматическое свойство позволит конструктору запрашивать 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;

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

person Will Ray    schedule 02.05.2016
comment
Первая часть вашего ответа верна. Но из Но является ли один из них предпочтительнее другого? вы больше не сосредотачиваетесь на простых типах, о которых идет речь. DateTime.Now не простой тип; это свойство (которое должно было быть реализовано как метод), которое возвращает простой тип. - person Mikkel R. Lund; 03.05.2016
comment
@MikkelR.Lund Я обновил ответ, чтобы сосредоточиться на конкретном примере в вопросе. - person Will Ray; 03.05.2016

Я подозреваю, что первый использует фоновое поле, тогда как второй превращается во что-то вроде «Правильно?»

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

А Expression-body — это упрощение синтаксиса для get body.

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

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

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

person Arturo Menchaca    schedule 02.05.2016
comment
Если ваше свойство должно быть неизменным, предпочтительнее использовать только начальное постоянное значение, а затем Auto-properity Initializer. Но так ли это на самом деле, если значение константы является простым типом, как в вопросе? - person Mikkel R. Lund; 03.05.2016
comment
@MikkelR.Lund: я думаю, что вы можете достаточно хорошо применить для простых типов, если ваше свойство ведет себя как константа, тогда используйте инициализаторы автоматических свойств, но если ваше свойство необходимо вычислить (например, Count), тогда Expression-Bodied - это только выбор. - person Arturo Menchaca; 03.05.2016