Создание локальной строки только для чтения C #

У меня есть локальная строка (путь к файлу), которую мне нужно получить из функции только один раз, и я хотел бы убедиться, что она больше никогда не будет изменяться. Я не могу использовать ключевое слово const, потому что значение моей строки определяется во время выполнения, а не во время компиляции. Поэтому я попытался использовать вместо этого ключевое слово readonly, но Visual Studio сообщает мне, что оно недопустимо для моего элемента. Как мне достичь желаемого уровня защиты, желательно без создания другого класса?

Для простоты и политики компании я (резко) сжал и переименовал свои классы и функции, но концепция осталась прежней.

public class myClass
{
    private void myFunction()
    {
      readonly string filePath = HelperClass.getFilePath("123");

     //do stuff
    }
}

public static class HelperClass
{ 
    public static string getFilePath(string ID)
    {
        switch(ID)
        {
             case "123":
                 return "C:/123.txt";

             case "234":
                 return "C:/234.txt";

             default:
                 throw new Exception(ID + " is not supported");
        }
    }
}

=== Редактировать для PS2Goat ====

public class myClass
{
    protected SomeObject o;
    private virtual readonly string path;        

    public myClass(someObject o)
    {
        this.o = o;
        path = HelperClass.getFilePath(o.getID());
    }

    private virtual void myFunction()
    { 

     //do stuff
    }
}

public class myDerivedClass
{
    private override virtual readonly string path;        

    public myDerivedClass(someObject o) : base(o)
    {
        path = HelperClass.getFilePath(o.getID()); //ID will be different
    }

    private override void myFunction()
    { 

     //do different stuff
    }
}





public static class HelperClass
{ 
    public static string getFilePath(string ID)
    {
        switch(ID)
        {
             case "123":
                 return "C:/123.txt";

             case "234":
                 return "C:/234.txt";

             default:
                 throw new Exception(ID + " is not supported");
        }
    }
}

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


person audiFanatic    schedule 30.07.2014    source источник
comment
Разве у вас нет свойства с логикой в ​​геттере / сеттере для предотвращения или ограничения повторного входа?   -  person Lee Willis    schedule 30.07.2014
comment
Строки неизменяемы, поэтому, если вы не назначите какой-либо другой экземпляр строки вашей переменной filePath, ее значение не изменится.   -  person Yurii    schedule 30.07.2014
comment
Я бы подумал об объявлении свойства readonly и его инициализации только один раз в конструкторе (или конструкторе типа), но только если метод getFilePath не требует значительного времени   -  person Fedor    schedule 30.07.2014
comment
@Yuriy Да, но я хочу, чтобы другим кодировщикам было сложно изменять значение, оно никогда не должно изменяться в рамках моей функции.   -  person audiFanatic    schedule 30.07.2014
comment
В дополнение к отмеченному дубликату см. stackoverflow.com/questions/2054761/.   -  person usr    schedule 30.07.2014


Ответы (5)


В методе не может быть переменных только для чтения. Поэтому его следует переместить в поле readonly static:

public class myClass
{
    private readonly static string filePath = HelperClass.getFilePath("123");
    private void myFunction()
    {    
      //do stuff
    }
}

Это приведет к инициализации вашей переменной filePath при первом доступе к myClass. Если это не то, что вам нужно, getFilePath - это длительная / дорогая операция, и вы хотите дождаться вызова myFunction, вы можете заменить реализацию на System.Lazy<T>:

public class myClass
{
    private readonly static Lazy<string> filePath 
            = new Lazy<string>(() => HelperClass.getFilePath("123")));
    private void myFunction()
    {   
      string path = filePath.Value;
      //do stuff
    }
}
person Bas    schedule 30.07.2014
comment
похоже, вы забыли указать ключевое слово static в первом примере - person Fedor; 30.07.2014

readonly означает, что его можно ТОЛЬКО установить в конструкторе классов или в экземпляре. Итак, вы можете изменить свою логику примерно так:

public class myClass
{
    private readonly string _filePath;

    public myClass()
    {
        _filePath = HelperClass.getFilePath("123");
    }

    private void myFunction()
    {
      // Use your _filePath here...

     //do stuff
    }
}

public static class HelperClass
{ 
    public static string getFilePath(string ID)
    {
        switch(ID)
        {
             case "123":
                 return "C:/123.txt";

             case "234":
                 return "C:/234.txt";

             default:
                 throw new Exception(ID + " is not supported");
        }
    }
}
person Belogix    schedule 30.07.2014
comment
НЕ ТОЛЬКО в конструкторе. Его можно установить только во время создания экземпляра, который включает встроенные настройки, такие как @njenson, сказал (это поле уровня класса): readonly string filePath = HelperClass.getFilePath("123"); - person ps2goat; 30.07.2014
comment
Теперь предположим, что свойство _filePath равно virtual. Я не уверен, что смогу еще это сделать. Дополнительным преимуществом превращения пути в свойство (помимо добавления моего уровня защиты), а не в поле, является то, что я могу его переопределить. Однако теперь предположим, что myClass наследуется myOtherClass и этот другой класс вызывает конструктор для myClass. Тогда, если предположить, что базовый класс (myClass) еще не поддерживается, тогда я получу исключение, верно? - person audiFanatic; 30.07.2014
comment
@ ps2goat В моем случае я не могу создать его встроенный экземпляр, потому что функция, которая извлекает идентификатор (он не закодирован жестко, как в моем примере), содержится в объекте, переданном в мой конструктор. Но это хорошая пища для размышлений. - person audiFanatic; 30.07.2014
comment
@audiFanatic, я только что объяснял, что ультиматум, установленный Belogix, неверен. Пока поле или свойство задаются из конструктора, все в порядке. При наследовании вы просто вызываете базовый конструктор из конструктора дочернего класса. Любой код, вызываемый в конструкторе, должен позволять вам установить этот член только для чтения. - person ps2goat; 30.07.2014
comment
хм, хорошо. Позвольте мне изменить свой вопрос, чтобы лучше проиллюстрировать то, что я пытаюсь сделать, а затем, надеюсь, мы сможем найти способ сделать это. Я отправлю ответ, когда правка будет сделана - person audiFanatic; 30.07.2014
comment
@ ps2goat Edit был сделан - person audiFanatic; 30.07.2014

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

public class myClass
    {
        readonly string filePath = HelperClass.getFilePath("123");

        private void myFunction()
        {

         //do stuff with filePath
        }
    }
person Nate Jenson    schedule 30.07.2014

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

Например:

    var data = new
    {
        FileName = HelperClass.getFilePath("123");
    };
person Savvas Kleanthous    schedule 30.07.2014

Переместите переменную filePath в поле, которое должно исправить ошибку. Локальные переменные не могут быть доступны только для чтения.

public class myClass
{
      readonly string filePath = HelperClass.getFilePath("123");
}
person Sriram Sakthivel    schedule 30.07.2014
comment
Что ж ... Ваш код идентичен вопросу, который, по его словам, не работает. И создание его доступным для записи полем не решает проблему, поскольку он хочет, чтобы оно было редактируемым, только если оно пустое. - person siva.k; 30.07.2014
comment
Нет, это не идентично вопросу, который, по его словам, не работает. Он продвинул его в поле, а не в локальную переменную, как это было в вопросе. - person Tim; 30.07.2014
comment
@ siva.k Мой код отличается от кода Op, возможно, вам придется внимательно прочитать его еще раз. Это сработает. - person Sriram Sakthivel; 30.07.2014