2010-09-07 3 views
12

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

+12

Лингвистический nitpick: «переменная, которая не может быть изменена» является оксюмороном. – LukeH

ответ

5

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

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; 
     } 
    } 
} 
+0

вы забыли некоторые кавычки –

+0

@Louis, обновили, он должен быть синтаксически правильным, но я набирал непосредственно как блок кода как часть ответа - я бы не рекомендовал SO as редактор кода - Нет интеграции управления версиями для одной вещи :) –

+0

Чтобы быть справедливым, я понимаю это, потому что это действительно с учетом требования в моем вопросе. Хотя на самом деле мне больше нравится ответ ck, и это на самом деле то, что мне нужно –

12

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

+0

так должно быть от конструктора? –

+0

Вы можете ограничить доступ и разрешить установку поля внутри своего класса, но 'readonly' и' const' - это единственные языковые конструкции, которые фактически позволяют устанавливать значение после построения или объявления. –

+1

Да, либо его конструктор, либо встроенный внутри объявления (но точно так же, как и const). Также он должен быть внутри конструктора, а не только во время построения - это означает, что невозможно установить значение переменной readonly внутри функции, называемой конструктором. –

4

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

т.е .: public readonly int z;

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

От MSDN:

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

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

  • public readonly int y = 5;

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

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

public string SetInClass 
{ 
    get; 
    private set; 
} 

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

1

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

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

5

Вы можете свернуть свой собственный, используя пользовательский сеттер (но не используйте 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; 
    } 
} 

EDIT:

Это Безразлично» t прекратить закрытие частного поля внутренними классами.

+1

@Kyle, Почему? если вы посмотрите на вышеизложенное, это говорит о том, что он не может быть установлен из конструктора, который исключает только чтение! выглядит как действительная реализация для меня, учитывая требования OP! – OneSHOT

+0

wow отличная идея! –

+2

@ Louis, так как Kyle и OneSHOT говорят, что нет ничего, что помешало бы классу изменять значение столько раз, сколько оно было бы, поскольку оно может напрямую обращаться к частной переменной. –

0

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

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

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

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

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

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

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

для деталей.

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

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

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

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

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

+1

+1 для объяснения немного больше о внутренних C# .. Очень образовательны! – Arcturus

+0

«Только декларации полей могут быть только для чтения, а C# не поддерживает объявленные пользователем локальные пользователи.» Хотя примитивные, строковые, enum и valueType производные локали могут быть объявлены * const *, что фактически одно и то же для этих типов (хотя, возможно, оно ближе к литеральному псевдониму, чем локальная переменная, доступная только для чтения). –

+0

@Paul: Но, конечно, это не * переменные *. Константы не являются переменными, потому что * constant * и * variable * являются * противоположностями *. (И вы ошибаетесь, чтобы сказать, что локали, основанные на ValueType, могут быть константами. Уверяю вас, они не могут. Я подозреваю, что вы думаете о каком-то другом языке, возможно, C++.) –

1

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

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

Usage будет

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

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

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

Смежные вопросы