Лучший способ защитить вспомогательное поле от ошибочного использования в C # - PullRequest
4 голосов
/ 18 февраля 2009

У меня есть класс (Foo), который лениво загружает свойство с именем (Bar). Какой ваш предпочтительный способ защиты от ошибочного использования (из-за интеллигенции или неопытного персонала) поля неинициализированной поддержки?

Я могу придумать 3 варианта:

  class Foo {
    // option 1 - Easy to use this.bar by mistake. 
    string bar;
    string Bar {
        get {
            // logic to lazy load bar  
            return bar; 
        }
    }

    // option 2 - Harder to use this._bar by mistake. It is more obscure.
    string _bar2;
    string Bar2 {
        get {
            // logic to lazy load bar2  
            return _bar2;
        }
    }

    //option 3 - Very hard to use the backing field by mistake. 
    class BackingFields {
        public string bar; 
    }

    BackingFields fields = new BackingFields();

    string Bar3 {
        get {
            // logic to lazy load bar  
            return fields.bar;
        }
    }

}

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

Обновление

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

Примечание Моя реализация блокируется на чтение, поскольку она поддерживает внешний набор.

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

Вы можете реализовать ленивый таким образом.

x = lazy do
    puts "<<< Evaluating lazy value >>>"
    "lazy value"
end

puts x
# <<< Evaluating lazy value >>>
# lazy value

Ответы [ 13 ]

5 голосов
/ 18 февраля 2009

Как насчет использования ObsoleteAttribute и #pragma - трудно пропустить это тогда!

    void Test1()
    {
        _prop = ""; // warning given
    }
    public string Prop
    {
#pragma warning disable 0618
        get { return _prop; }
        set { _prop = value; }
#pragma warning restore 0618
    }
    [Obsolete("This is the backing field for lazy data; do not use!!")]
    private string _prop;
    void Test2()
    {
        _prop = ""; // warning given
    }
4 голосов
/ 18 февраля 2009

Вариант 5

Lazy<T>

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

Добавление [EditorBrowsable (EditorBrowsableState.Never)] к полю не поможет, если оно является закрытым, поскольку эта логика включает только intellisense, сгенерированный из метаданных, а не текущий код (текущий проект и все, что делается с помощью ссылок на проекты, а не DLL).

Примечание: Lazy<T> не является потокобезопасным (это хорошо, нет смысла блокировать, если вам это не нужно), если вам нужна безопасность потоков, либо используйте один из потоковобезопасных от Joe Duffy или Параллельные расширения CTP

3 голосов
/ 18 февраля 2009

Обычно я выбираю вариант 2, так как позже ошибки легче обнаружить, хотя вариант 1 пройдет проверку кода. Вариант 3 кажется запутанным и, хотя он может работать, не будет хорошим кодом пересмотреть 6 месяцев подряд, пытаясь реорганизовать / исправить ошибку / и т.д.

2 голосов
/ 18 февраля 2009

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

2 голосов
/ 18 февраля 2009

Вариант 1 в сочетании с некоторым образованием.

Обоснование: программное обеспечение предназначено для чтения чаще, чем написано, поэтому оптимизируйте его для общего случая и оставляйте его читаемым.

1 голос
/ 28 февраля 2009

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

/************************************************************
* Note: When updating this class, please take care of using *
*       only the accessors to access member data because of *
*       ... (state the reasons / your name, so they can ask *
*       questions)                                          *
*************************************************************/

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

1 голос
/ 18 февраля 2009

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

0 голосов
/ 17 апреля 2009

Это похоже на попытку спланировать ошибки, которые могут вообще не произойти, и в основном это беспокоит не то, что нужно.

Я бы выбрал вариант 1 + комментарии:

///<summary>use Bar property instead</summary> 
string bar;

///<summary>Lazy gets the value of Bar and stores it in bar</summary>
string Bar {
    get {
        // logic to lazy load bar  
        return bar; 
    }
}

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

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

И если вы все еще действительно беспокоитесь об этом, создайте правило FxCop (или что-то еще, что вы используете), чтобы проверять подобные вещи.

0 голосов
/ 17 апреля 2009

Это может быть слишком просто, но почему бы не перевести все ленивые в базовый класс

 public class LazyFoo{
      private string bar;
      public string Bar{
         get{
             // lazy load and return
         }
         set {
             // set
         }
      }
    }

    public class Foo : LazyFoo{
      // only access the public properties here
    }

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

0 голосов
/ 17 апреля 2009

В настоящее время я нахожусь в аналогичной ситуации. У меня есть поле, которое должно использоваться только его средством доступа к свойству.
Я не могу использовать автоматические свойства, так как мне нужно выполнить дополнительную логику, когда свойство установлено. (Свойство загружается не лениво).

Не было бы замечательно, если бы следующая версия C # позволяла что-то вроде этого:

public class MyClass
{
    public int MyProperty
    {
        int _backingField;

        get
        {
            return _backingField;
        }
        set
        {
            if( _backingField != value )
            {
                _backingField = value;
                // Additional logic
                ...
            }
        }
    }
}

При такой конструкции область действия переменной _backingField ограничивается свойством. Я хотел бы видеть похожую языковую конструкцию в следующей версии C #:)

Но, боюсь, эта функция никогда не будет реализована: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=381625

...