переменная, которая не может быть изменена - PullRequest
13 голосов
/ 07 сентября 2010

Разрешает ли C # переменную, которую нельзя изменить?Это похоже на const, но вместо того, чтобы назначать ему значение при объявлении, переменная не имеет никакого значения по умолчанию, но может быть присвоена только один раз во время выполнения (EDIT: и, возможно, не из конструктора).или это невозможно?

Ответы [ 9 ]

24 голосов
/ 07 сентября 2010

Да, есть несколько способов сделать это в C #.

Прежде всего, что такое "переменная"? Переменная является местом хранения. Локальные переменные, формальные параметры методов (и индексаторы, конструкторы и т. Д.), Статические поля и поля экземпляров, элементы массива и разыменования указателей являются переменными.

Некоторые переменные могут быть объявлены как «только для чтения». Переменная «только для чтения» может быть изменена только один раз, либо инициализатором в объявлении, либо в конструкторе. Только объявления полей могут быть только для чтения; C # не поддерживает объявленные пользователем локальные файлы только для чтения.

Существуют определенные ограничения на переменные только для чтения, которые помогают гарантировать, что нормальная работа C # не приводит к мутации. Это может привести к неожиданным результатам! См

http://ericlippert.com/2008/05/14/mutating-readonly-structs/

для деталей.

Некоторые местные жители также эффективно читаются. Например, когда вы говорите using(Stream s = whatever), то внутри встроенного оператора using вы не можете изменить значение s. Причина этого ограничения состоит в том, чтобы предотвратить ошибку, при которой вы создаете ресурс, который должен быть удален, а затем утилизируете другой ресурс, когда содержимое переменной s удаляется. Лучше быть таким же.

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

Локальная переменная, объявленная в операторе foreach, также фактически readonly - эта переменная меняет значение каждый раз в цикле, но вам не разрешено изменять ее значение.

Нет способа сделать разыменование только для формального параметра, элемента массива или указателя.

Существуют различные способы «снять» ограничение на чтение и запись в переменную, которая должна быть только для чтения. Вы можете использовать Reflection или небезопасный код, чтобы нарушить практически любое ограничение безопасности CLR, если у вас достаточно прав для этого. Если тебе больно, когда ты это делаешь, не делай этого; с этими полномочиями приходит ответственность знать, что ты делаешь, и делать это правильно.

12 голосов
/ 07 сентября 2010

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

5 голосов
/ 07 сентября 2010

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

public class SetValueOnce<T>
{
    public bool _set;
    private T _value;

    public SetValueOnce()
    { 
      _value = default(T);
      _set = false;
    }

    public SetValueOnce(T value)
    { 
      _value = value;
      _set = true;
    }

    public T Value
    {
      get
      {
          if(!_set)
             throw new Exception("Value has not been set yet!");
          return _value;
      {
      set
      {
         if(_set)
             throw new Exception("Value already set!");
         _value = value;
         _set = true;
      }
   }
}
5 голосов
/ 07 сентября 2010

Вы можете бросить свой собственный, используя пользовательский установщик (но не используйте Object, если нет необходимости, выберите правильный класс):

private Object myObj = null;
private Boolean myObjSet = false;

public Object MyObj
{
    get { return this.myObj; }
    set 
    { 
        if (this.myObjSet) throw new InvalidOperationException("This value is read only");
        this.myObj = value;
        this.myObjSet = true;
    }
}

РЕДАКТИРОВАТЬ:

Это не останавливает изменение закрытого поля внутренними объектами класса.

4 голосов
/ 07 сентября 2010

Конечно.Вы можете использовать readonly:

Т.е.: public readonly int z;

Это можно изменить только из конструктора.

Из MSDN :

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

Когда переменная инициализируется в объявлении, например:

  • public readonly int y = 5;

  • Для поля экземпляра в конструкторах экземпляров класса, который содержит объявление поля, или для статического поля, в статическом конструкторе класса, который содержит поледекларация.Это также единственные контексты, в которых допустимо передавать поле только для чтения в качестве параметра out или ref.

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

public string SetInClass
{
   get;
   private set;
}

Это позволяет вносить изменения в классе, но переменная не может быть изменена извнекласс.

2 голосов
/ 21 марта 2017

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

public class Immutable<T> {
    public T Val { get; private set; }
    public Immutable(T t) {
        Val = t;
    }
}

Использование будет

var immutableInt1 = new Immutable<int>(3); // you can set only once
immutableInt1.Val = 5; // compile error here: set inaccessible
Console.WriteLine("value: " + immutableInt1.Val);

Дело в том, что теперь вы получите ошибку compile , если попытаетесь установить новое значение.

Кроме того, я считаю, что вам лучше использовать функциональный язык, такой как F # вместо C #, если вы хотите следовать этой парадигме.

1 голос
/ 07 сентября 2010

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

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

1 голос
/ 07 сентября 2010

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

Читайте об этом здесь: http://msdn.microsoft.com/en-us/library/acdd6hb7%28v=VS.100%29.aspx

0 голосов
/ 07 сентября 2010

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

private myVariable = null;
public object MyVariable
{
   get { return myVariable; }
   set { if(myVariable == null ) myVariable = value;
}
...