Как установить поле readonly в метод initialize, который вызывается из конструктора?
Я уверен, что я где-то видел, что могу сделать следующее, используя атрибут выше моего метода Init(), который сообщает компилятору, что метод Init() должен быть вызван только из конструктора, что позволяет поле readonly. Я забыл, что называется атрибутом, и я не могу найти его в 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;
}
}
Ответы
Ответ 1
Ответ Роба - это способ сделать это, в моей книге. Если вам нужно инициализировать несколько полей, вы можете сделать это с помощью параметров 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
). Но может быть, это только я.
Ответ 2
Нет, вы не можете. В принципе, вы должны выполнить инициализацию в конструкторе - что он там для.
Для повторного использования, если у вас есть несколько конструкторов, попробуйте сделать все, кроме одного, цепочкой, в "главный" конструктор, который выполняет всю реальную работу.
Если какое-либо из значений требует сложного вычисления, в любом случае, возможно, нецелесообразно вычислять в конструкторе, и если это так, вы можете поместить часть вычисления в частный метод, который возвращает значение, затем вызовите этот метод из конструктора.
EDIT: я бы не включил отражение как реальный способ сделать это. Существуют всевозможные заявления, которые вы можете сделать, которые не включают злоупотребления посредством отражения. Хотите, чтобы строковый литерал "x" превратился в "y"? Конечно, вы можете сделать это с отражением, если у вас есть правильные разрешения... но вы абсолютно не должны.
Ответ 3
Вместо использования метода 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
}
}
Это может быть немного поздно, чтобы добраться до нужного вам оригинального человека, но похоже, что это решительно решит проблему... Без необходимости использовать какие-либо неприятные отражения или параметры.
Ответ 4
Единственное решение, о котором я могу думать, - это вернуть значение из метода Init()
, которому должно быть присвоено поле readonly
:
public class Class
{
private readonly int readonlyField;
public Class()
{
readonlyField = Init();
}
private int Init()
{
return 1;
}
}
Ответ 5
Это невозможно. Поля, помеченные readonly
, могут быть установлены только из конструктора
Ответ 6
Джаред прав; это невозможно. Обходные пути, о которых я могу думать, следующие:
- Инициализировать поле в объявлении.
- Инициализировать поле в конструкторе (вручную встраивать свой метод
Init
).
- Присвоить поле значение, возвращаемое методом, например:
_myField = GetInitialMyFieldValue();
- Передайте поле методу
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);
}
Ответ 7
То, что я закончил в текущей технологии (С# 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);
}
}
Не самый чистый, но мне кажется чище.
Ответ 8
Компилятор С# позволяет вам устанавливать только поля readonly, если вы инициализируете их inline:
private readonly int readonlyField = 1;
или от конструктора:
public Class()
{
readonlyField = 1;
}
Ответ 9
Я думаю, что это работает, если использовать 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 является открытым, а не частным. Я знаю, что вы можете редактировать частное поле, но я не уверен, почему он не работает для меня!
Ответ 10
Как насчет инициализированного свойства только с геттером (начиная с С# 6.0)?
private int MyProperty { get; } = 0;
Ответ 11
Я знаю, что уже поздно, но как насчет использования класса в качестве возвращаемого значения вместо использования параметров, если есть необходимость инициализировать несколько полей, есть ли недостатки? Имхо, это более удобно и более читабельно, чем использование вложенных конструкторов.
public class MyInitClass
{
public int Field1 { get; set; }
public int Field2 { get; set; }
}
public class Class
{
private readonly int readonlyField1;
private readonly int readonlyField2;
public Class()
{
var init = Init();
readonlyField1 = init.Field1;
readonlyField2 = init.Field2;
}
private MyInitClass Init()
{
return new MyInitClass() { Field1 = 1, Field2 = 2 };
}
}