Почему нельзя иметь только один аксессуар с телом вместо двух?

Предположим, у меня было это свойство:

    public int Money
    {
       get;
       set{
         Money = value;
       }
    }

Это не будет компилироваться, говоря, что метод доступа get должен иметь тело, потому что он не помечен как абстрактный, внешний или частичный. Если я добавлю к нему тело и верну свойство Money следующим образом:

    public int Money
    {
       get{
         return Money;
       }
       set{
         Money = value;
       }
    }

.. У меня на руках будет бесконечный цикл, и моя программа выдаст исключение переполнения стека.

Итак, мой вопрос в конечном итоге сводится к следующему: есть ли способ сохранить методы доступа get / set, вернуть текущее значение в get без создания бесконечного цикла и по-прежнему иметь тело для метода доступа set?


person William Evenius    schedule 10.09.2015    source источник
comment
Почему вы используете метод доступа для int?   -  person Robbert    schedule 10.09.2015
comment
Я думаю, вы можете неправильно понять, как работают свойства.   -  person Moo-Juice    schedule 10.09.2015
comment
Похоже, вы тут смешиваете две разные вещи. Бесконечный цикл возникает из-за того, что в установщике вашего свойства вы присваиваете что-то свойству (а не частному полю). Тело необходимо добавить либо для получателя и установщика (в этом случае вы контролируете, что происходит с назначенным значением и откуда берется полученное значение), либо ни к чему (в этом случае компилятор будет автоматически добавить частное поле, которое будет записано / прочитано в установщике и получателе соответственно.   -  person O. R. Mapper    schedule 10.09.2015
comment
@Robbert Это был всего лишь пример, но я хочу в основном выполнять дополнительные задачи со значением, когда оно установлено, но ничего, когда оно читается.   -  person William Evenius    schedule 10.09.2015
comment
@Robbert: Почему ему не использовать аксессор для int?   -  person O. R. Mapper    schedule 10.09.2015


Ответы (3)


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

Это автоматически реализуемое свойство:

public int Foo { get; set; }

Что сгенерирует поле поддержки при компиляции. В IL это будет выглядеть примерно так:

private int BackingField_Foo;
public int Foo
{
    get { return BackingField_Foo; }
    set { BackingField_Foo = value; }
}

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

Теперь, если вы сами реализуете один из средств доступа, чтобы самостоятельно писать в самоопределяемое поле, это больше не автоматически реализуемое свойство, поэтому вам придется реализовать и другой метод доступа (если вы его объявляете; вы может создать обычное свойство только для чтения или записи).

Ваш текущий код выбрасывает StackOverflowExceptions, потому что аксессоры обращаются к себе, до бесконечности.

См. Также Правильное использование свойств C #.

person CodeCaster    schedule 10.09.2015
comment
Хотя это правильно, он не дает ответа на почему. (Очевидно, потому что компилятор не может знать, какое значение вернуть из получателя, когда ваш пользовательский сеттер записывает присвоенное значение где-нибудь.) - person O. R. Mapper; 10.09.2015
comment
Мне интересно, почему не поддерживается некоторый синтаксис типа public int SomeProperty { set { self = value; } get; }, поэтому вы можете сочетать преимущества автоматически реализуемых свойств с явными средствами доступа. - person CodeCaster; 10.09.2015
comment
@CodeCaster, согласен. Я ловил себя в этой ловушке более пары раз, прежде чем понял, что пытаюсь сделать что-то глупое. - person Broots Waymb; 10.09.2015
comment
(И нет, я не действительно удивляюсь, почему такой синтаксис не поддерживается, потому что были более приятные вещи, которые можно было реализовать, и преимущества не перевешивали затрат ;-). Тот случай, когда я бы его использовал (INotifyPropertyChanged), легко исправить с помощью сниппетов или шаблонов T4. - person CodeCaster; 10.09.2015

Либо используйте:

public int Money { get; set; }

или если вам действительно нужно иметь тело для аксессоров, вам нужно использовать поле поддержки:

private int _money;
public int Money
{
    get { return _money; }
    set { _money = value; }
}

Однако последний используется только в том случае, если вам нужно выполнить некоторую дополнительную логику (например, вызвать событие) при использовании геттера или сеттера.

Кроме того, последнее более или менее то, что компилятор генерирует для вас автоматически, и может гарантировать, что резервное поле используется последовательно.

Если вы предоставляете только одно тело, становится трудно определить, как оно должно себя вести: в конце концов, у вас нет доступа к сгенерированному вспомогательному полю в вашем коде, поэтому вся идея не имеет смысла.

person BartoszKP    schedule 10.09.2015

Вы можете сделать одно из следующего:

// 1. a public variable
public int Money;

// 2. an auto-implemented, or implicit property (very much like the above)
public int Money { get; set; }

// 3. An explicit property declaration with a private variable
private int _money;

public int Money {
    get {return _money;}
    set {
        _money = value;

        // possibly do something else here
    }
}
person GPicazo    schedule 10.09.2015