Правильное кодирование прямой доступ к резервному полю свойства C#


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

Рассмотрим:

class MyClass
{
    private string m_MySuperString;
    public string MySuperString
    {
        get { return m_MySuperString; }
        set { m_MySuperString = value; }
    }

    public void MyMethod()
    {
        if (blah != yada)
        {
             m_MySuperString = badabing;
        }
    }

    public void MyOtherMethod()
    {
        if (blah == yada)
        {
            m_MySuperString = badaboom;
        }
    }
}

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

Пожалуйста, какие-нибудь мысли ? Я посмотрел на эту ссылку на SO и другие> зачем использовать закрытые члены, а затем использовать открытые свойства для их установки?

EDIT

Позвольте мне быть ясным, так как есть хорошая информация предоставляется и, скорее, отвечать на все ответы и комментарии непосредственно. Я не спрашиваю о том, для чего предназначены свойства, не могу ли я сделать автоматически реализованные свойства, частные сеттеры, уведомления OnValueChange, логика в свойствах. Мой вопрос касается прямого доступа к этому резервному полю - например, если у вас есть, скажем, сценарий mutlithreaded - не весь ли смысл synclock на геттерах / сеттерах-контролировать доступ к бэкингфилду ? Будет ли такой код приемлемым в этом сценарии-просто добавление syncLock к геттеру и сеттеру ?? Имейте в виду, что код в конструкторе myClass является примером-код может быть в любом дополнительный метод - например, обновленный класс-Method1

END EDIT

3 3

3 ответа:

Я рекомендую проверить Главу 8, Раздел 1 C# в глубине @JonSkeet (из которого я бесстыдно взял нижеприведенные фрагменты для целей обучения) для получения дополнительной информации о автоматически реализуемых свойствах. Короче говоря, ответ на ваш вопрос: нет, в этом коде нет ничего плохого.

Рассмотрим следующий фрагмент:

public string Name { get; set; }

Компилируется как

private string <Name>k__BackingField;
public string Name
{
     get { return <Name>k__BackingField; }
     set { <Name>k__BackingField = value; }
}

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

//code above, plus
private static int InstanceCounter { get; set; }
private static readonly object counterLock = new object();

public InstanceCountingPerson(string name, int age) {
    Name = name;
    Age = age;
    lock (counterLock) // safe property access
    {
        InstanceCounter++;
        // and whatever else you have to do with the lock enabled
    }
}

-который является шаблоном, также упоминаемым в этом вопросе SO. Однако, как было указано выше, блокировки (а) потенциально медленны, (б) не могут гарантировать, что их работа будет выполнена, потому что они должны быть освобождены в какой-то момент, и (в) полагаются на систему доверия, потому что они вроде как наивно предполагают, что все, кто хочет получить доступ к этому объекту, сделает это. правильное использование замка (не всегда верно, по крайней мере, в некоторых кодах, которые я видел :D ). Преимущество методов getter и setter заключается в том, что вы можете применить шаблон использования блокировки (читай: правильно инкапсулировать поле, как предлагали другие) для любого экземпляра вашего класса.

Однако другой паттерн, который вы можете рассмотреть, - это инверсия контроля. С помощью контейнера инъекции зависимостей можно задать уровень потокобезопасности, который вам удобен. Если вы удобно, когда все ждут одноэлементного экземпляра объекта, вы можете объявить, что все ссылки на интерфейс объекта ссылаются на один и тот же объект (и должны ждать, пока объект станет доступным), или вы можете определить, что потокобезопасный экземпляр объекта должен создаваться каждый раз, когда он запрашивается. Смотрите этот так называемый ответ Для получения более подробной информации.

Примечание:

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

Свойства в объектно-ориентированном программировании (ООП) помогают обеспечить инкапсуляцию . Идея заключается в том, что только сам объект может взаимодействовать со своими собственными данными (т. е. полями). Доступ к данным объекта извне разрешен только через методы. В Java, например, вы должны явно написать get-и set-методы. Свойства В C# имеют специальный синтаксис, который объединяет оба этих метода в одной конструкции, но геттеры и сеттеры на самом деле являются методами.

Это это также означает, что объекту абсолютно разрешен прямой доступ к собственным полям.

Однако есть случаи, когда геттеры и сеттеры свойств выполняют дополнительную логику. Сеттер может вызвать событие PropertyChanged или выполнить некоторую проверку. Геттер может объединять несколько полей или давать форматированное или вычисленное значение. Если вам нужна эта дополнительная логика, то вы должны получить доступ к свойствам, а не к полям. Если свойство реализовано автоматически, то у вас нет выбора (в C#), так как резервное поле скрыто и недоступно. (в VB он скрыт от IntelliSense, но доступен изнутри класса .)

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

public string MySuperString{ get;  set ;}

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

Общественная строка имя{ получить { возвращение имя + фамилия} }

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