Как установить поле только для чтения в методе инициализации, который вызывается из конструктора? - PullRequest
34 голосов
/ 16 сентября 2010

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

public class Class
{
    private readonly int readonlyField;

    public Class()
    {
        Init();
    }

    // Attribute here that tells the compiler that this method must be called only from a constructor
    private void Init()
    {
        readonlyField = 1;
    }
}

Ответы [ 8 ]

39 голосов
/ 16 сентября 2010

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

public class Class
{
    private readonly int readonlyField1;
    private readonly int readonlyField2;

    public Class()
    {
        Init(out readonlyField1, out readonlyField2);
    }

    protected virtual void Init(out int field1, out int field2)
    {
        field1 = 1;
        field2 = 2;
    }
}

Лично я считаю, что это имеет смысл в определенных сценариях, например, когда вы хотите, чтобы ваши поля были readonly, но вы также хотят иметь возможность устанавливать их по-разному в производном классе (без необходимости связывать тонну параметров через некоторый конструктор protected).Но, возможно, это только я.

13 голосов
/ 04 июля 2011

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

public class MyClass
{
    readonly int field1;
    readonly double field2;
    public MyClass(int field1, double field2)
    {
        //put whatever initialization logic you need here...
        field1 = 10;
        field2 = 30.2;
    }
    public MyClass(int field1, double field2p1, double field2p2)
        : this(field1, (field2p1 + field2p2))
    {
        //put anything extra in here
    }
}

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

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

Единственное решение, которое я могу придумать, - это вернуть значение из метода Init(), которому необходимо присвоить поле readonly:

public class Class
{
    private readonly int readonlyField;

    public Class()
    {
        readonlyField = Init();
    }

    private int Init()
    {
        return 1;
    }
}
6 голосов
/ 16 сентября 2010

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

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

Джаред прав; это невозможно. Обходные пути, которые я могу придумать:

  1. Инициализировать поле в объявлении.
  2. Инициализируйте поле в конструкторе (вручную вставьте свой метод Init).
  3. Присвойте полю значение, возвращаемое методом, например: _myField = GetInitialMyFieldValue();
  4. Передайте поле методу Init с модификатором out. Это может быть полезно, если у вас есть много полей для инициализации, которые зависят от параметров конструктора. Э.Г.

 private readonly int _x;
 private readonly string _y;

 private void Init(int someConstructorParam, out int x, out string y){ .. }

 public Class(int someConstructorParam)
 {
     Init(someConstructorParam, out _x, out _y);
 } 
2 голосов
/ 06 апреля 2018

То, что я закончил в текущей технологии (C # 7.x), - это использование системы кортежей значений:

public class MyClass
{
    private readonly int x;
    private readonly int y;
    private readonly int z;

    public MyClass(int x)
    {
        this.x = x;
        (y, z) = InitYandZ();
    }

    private (int, int) InitYandZ()
    {
        return (5, 10);
    }
}

Не самый чистый, но мне кажется чище.

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

C # компилятор позволяет вам устанавливать поля только для чтения, если вы инициализируете их встроенными:

private readonly int readonlyField = 1;

или из конструктора:

public Class()
{
    readonlyField = 1;
}
0 голосов
/ 16 сентября 2010

Я думаю, что это работает, если использовать Reflection. На самом деле это работает для меня:

    public class Class
        {
            private readonly int readonlyField;
        public int MyField()
        {
             return readonlyField;
        }
        public Class()
        {
            readonlyField = 9;
        }
    }

и

static void Main(string[] args)
        {

            Class classObj = new Class();
            Console.WriteLine(classObj.MyField());//9

            Misc.SetVariableyByName(classObj, "readonlyField", 20);//20
            Console.WriteLine(classObj.MyField());
         }

это SetVariableByName ():

public static b

ool SetVariableyByName(object obj, string var_name, object value)
            {
                FieldInfo info = obj.GetType().GetField(var_name, BindingFlags.NonPublic| BindingFlags.Instance);
                if (info == null)
                return false;
            /* ELSE */
            info.SetValue(obj, value);
            return true;          
        }

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...