Init()メソッドの上にある属性を使用して、次のことができることをどこかで見たことがあると確信しています。これにより、コンパイラーに、Init()メソッドはコンストラクターからのみ呼び出す必要があるため、読み取り専用フィールドで次のことが可能になります。悩ませる。属性の名前を忘れてしまいましたが、グーグルで見つけられないようです。
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;
}
}
ロブの答え 私の本ではそれを行う方法です。複数のフィールドを初期化する必要がある場合は、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
コンストラクターを介して大量のパラメーターをチェーンする必要はありません)。しかし、多分それは私だけです。
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
}
}
これは、困っている元の人に到達するのに少し遅れるかもしれませんが、これは問題をきれいに解決するようです...どんな種類の厄介な反射や出力パラメータを使用する必要もありません。
私が考えることができる唯一の解決策は、readonly
フィールドに割り当てる必要があるInit()
メソッドから値を返すことです。
public class Class
{
private readonly int readonlyField;
public Class()
{
readonlyField = Init();
}
private int Init()
{
return 1;
}
}
これはできません。 readonly
でタグ付けされたフィールドは、コンストラクターからのみ設定できます
Jaredは正しいです。これは不可能です。私が考えることができる回避策は次のとおりです。
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);
}
私が現在の技術(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);
}
}
どちらもきれいではありませんが、私にはきれいに見えます。
C#コンパイラでは、読み取り専用フィールドをインラインで初期化する場合にのみ、それらを設定できます。
private readonly int readonlyField = 1;
またはコンストラクターから:
public Class()
{
readonlyField = 1;
}
ゲッターのみで初期化されたプロパティはどうですか(C#6.0以降)?
private int MyProperty { get; } = 0;
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はプライベートではなくパブリックであるということです。プライベートフィールドを編集できることは知っていますが、なぜそれが機能しないのかわかりません!
これが遅いことは知っていますが、複数のフィールドを初期化する必要がある場合、パラメータを使用する代わりにクラスを戻り値として使用するのはどうですか?欠点はありますか?これは、ネストされたコンストラクターを使用するよりも便利で読みやすいです。
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 };
}
}