Поле только для чтения, которому можно присвоить значение вне конструктора - PullRequest
2 голосов
/ 25 ноября 2010

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

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

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

Спасибо за ваш интерес.

Ответы [ 7 ]

4 голосов
/ 25 ноября 2010

Вы не получите ошибку времени компиляции, но вы можете выполнить то, что вы ищете, с помощью свойства. В установщике устанавливайте значение только в том случае, если внутреннее значение bool / flag имеет значение false. Как только вы установите его в первый раз, установите флаг в true. И если сеттер снова вызывается другим фрагментом кода, вы можете выдать исключение InvalidOperationException, чтобы сообщить им, что оно уже инициализировано

private bool fieldAlreadySet = false;
private string field;

public string Field
{
   get {return field;}
   set
   {
       if (fieldAlreadySet) throw new InvalidOperationException("field already set");
       this.field = value;
       fieldAlreadySet = true;
   }
}
1 голос
/ 25 ноября 2010

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

public struct WriteOnce<T>
{
    private T? _value;

    public T Value
    {
        get { return _value; }
        set
        {
            if (_value.HasValue) throw new InvalidOperationException();
            _value = value;
        }
    }
}

EDIT Я просто понял, что вышеупомянутое даже не скомпилируется. Компилятор не позволяет типу _value быть Nullable, потому что T в Nullable должен быть ненулевым типом.

Вот реализация, которая будет работать немного лучше:

public struct WriteOnce<T>
{
    private T _value;
    private bool _hasValue;

    public bool HasValue { get { return _hasValue; } }

    public T Value
    {
        get { return _value; }
        set
        {
            if (HasValue) throw new InvalidOperationException();
            _value = value;
            _hasValue = true;
        }
    }

    public static implicit operator T(WriteOnce<T> x)
    {
        return x.Value;
    }

    public WriteOnce(T val)
    {
        _value = val;
        _hasValue = true;
    }
}

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

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

WriteOnce<int> foo = default(WriteOnce<int>);

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

private WriteOnce<int> _someInt = default(WriteOnce<int>);
public int SomeInt
{ 
    get { return _someInt; }
    set { _someInt.Value = value; }
}

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

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

1 голос
/ 25 ноября 2010

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

0 голосов
/ 04 ноября 2011

Я думаю, что нашел способ сделать это, если это делается в конструкторе.Это также может быть в строке объявления переменной.Если это сделано вне этих контекстов, вы получите сообщение об ошибке: Поле, доступное только для чтения, не может быть назначено (кроме как в конструкторе или инициализаторе переменной)

0 голосов
/ 25 ноября 2010

Это выглядит как хороший кандидат для назначения объявления или синглтона.

public class SomeClass {
    private readonly Type t = typeof(SomeClass);
}

Или иначе, как сказали другие, создайте внутреннее или около того свойство только с помощью установщика, а затем присвойте ему значение.

public class SomeClass {
    private Type t;

    internal Type Type {
        set {
            if (t == null) t = value;
        }
    }
}
0 голосов
/ 25 ноября 2010

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

0 голосов
/ 25 ноября 2010

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

int? _writeOnceField;

private int? WriteOnce
{
   set
   {
      if (!_writeOnceFiled.HasValue)
      {
        writeOnceFiled = value;
      }
      else
      {
        // Throw exception
      }
   }
}
...