Как я могу сделать версию только для чтения класса?

У меня есть класс с различными общественными собственностями, которые я позволяю пользователям редактировать через сетку свойства. Для персистентности этот класс также сериализируется/десериализовывается к/от XML-файлу через DataContractSerializer.

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

Мой класс имеет булево свойство, которое определяет, должен ли класс быть только для чтения, но действительно ли возможно использовать это свойство для так или иначе, динамично добавляют атрибуты "только для чтения" к свойствам класса во времени выполнения? Если не, что такое альтернативное решение? Я должен перенести свой класс в класс обертки только для чтения?

17
задан Eric Anastas 27 April 2010 в 20:14
поделиться

6 ответов

Неизменяемость - это область, в которой C # еще есть над чем работать. Хотя создание простых неизменяемых типов со свойствами только для чтения возможно, как только вам понадобится более сложный контроль над изменяемыми типами, вы начнете сталкиваться с препятствиями.

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

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

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

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

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

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

Третий вариант является наиболее сильным с точки зрения обеспечения соблюдения, но он может привести к дублированию кода и требует скорее рефакторинга. Часто бывает полезно комбинировать варианты 2 и 3, чтобы сделать связь между оболочкой только для чтения и изменяемым типом полиморфной.

Лично я предпочитаю третий вариант при написании нового кода, когда я ожидаю, что потребуется обеспечить неизменность. Мне нравится тот факт, что невозможно "отбросить" неизменяемую оболочку, и это часто позволяет избежать написания беспорядочных проверок исключения if-read-only-throw в каждый установщик.

20
ответ дан 30 November 2019 в 13:12
поделиться

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

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

0
ответ дан 30 November 2019 в 13:12
поделиться

Вот такая помощь:

class Class1
{
    private bool _isReadOnly;

    private int _property1;
    public int Property1
    {
        get
        {
            return _property1;
        }
        set
        {
            if (_isReadOnly) 
              throw new Exception("At the moment this is ready only property.");
            _property1 = value;
        }
    }
}

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

Надеюсь, это то, что вы ищете.

0
ответ дан 30 November 2019 в 13:12
поделиться

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

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

1
ответ дан 30 November 2019 в 13:12
поделиться

Почему бы не что-то вроде:

private int someValue;
public int SomeValue
{
    get
    {
         return someValue;
    }
    set
    {
         if(ReadOnly)
              throw new InvalidOperationException("Object is readonly");
         someValue= value;
    }
2
ответ дан 30 November 2019 в 13:12
поделиться

Вы можете использовать PostSharp для создания OnFieldAccessAspect, который не будет передавать новое значение ни в какое поле, если для _readOnly будет установлено значение true. С аспектным кодом повторение отсутствует, и ни одно поле не будет забыто.

0
ответ дан 30 November 2019 в 13:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: