◆ オブジェクト初期化子見やすいのにコンストラクタ内での実行にならないから 制限も多い
◆ readonly / getter のみのフィールド/プロパティに代入できない

new Class
{
val1 = 1,
val2 = "abc",
}

と言う感じでプロパティを設定できるのですが これはコンストラクタ実行のあとで プロパティやフィールドへの代入をしてるものと一緒なので private に書き込みや getter のみ/readonly なものに書き込みができません

private はまぁいいのですけど 初期化子なのに readonly なところを初期化の書き込みできないのはかなり不便です

インスタンス作るときに設定して絶対に変えないものはできるだけ readonly にしておきたいです
かと言って コンストラクタに全部渡すのは見た目的にもコード量的にも嫌です

コンストラクタ中で実行される版の初期化子が早くできて欲しいのですが 今のところはどうしようもないのでとりあえずな代替策です

1) コンストラクタを自力で頑張る

public class Class
{
    public int val1 { get; }
    public string val2 { get; }
    public bool val3 { get; }
    ...

    public Class(int val1 = default(int), string val2 = default(string), bool val3 = default(bool), ...)
    {
        this.val1 = val1;
        this.val2 = val2;
        this.val3 = val3;
        ...
    }
}

こんな感じで コンストラクタに全部書きます
「…」 は続くってことで構文的にはエラーです

使う側はちょっと楽できて
public void usage()
{
    var instance = new Class(
        val1: 1,
        val2: "a",
        val3: false
    );
}

順番通りじゃなくて名前付き引数を使います

定義が大変だけど使う側は使いやすいです

2) コンストラクタをリフレクションで

さすがに書いてられないので匿名型を渡して 自動でセットするようにしたいです
public Class(object arg)
{
// ここでリフレクションで this のメンバに代入する
}

というのを想定します

でもこれをクラスごとに書くのはコピペ済むにしても避けたいです
リフレクションなのでどのクラスでもつかえるわけですから
public Class(object arg)
{
    AutoInit.init(this, arg);
}

これで済ませてしまいたいです

さっそくこれで作っていると問題が


readonly ってコンストラクタの中から呼び出したメソッドでも設定できません
ついでに private も外部からのアクセスなのでできません


内部からしかできないなら親クラスの機能としてしまって 継承するしかなさそう
public class AutoInit
{
    public AutoInit(object arg)
    {
        foreach (var prop_info in arg.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
        {
            var name = prop_info.Name;
            var value = prop_info.GetValue(arg);

            var field = this.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if(field != null)
            {
                field.SetValue(this, value);
                continue;
            }

            var property = this.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (property != null)
            {
                var backing_field = this.GetType().GetField($"<{name}>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
                if (backing_field == null) continue;
                backing_field.SetValue(this, value);
                continue;
            }
        }
    }
}

これを継承したクラスはオブジェクト初期化子風な匿名型オブジェクトで初期化できます
public abstract class Class : AutoInit
{
    public readonly int a;
    public string b { get; }

    public Class(object arg) : base(arg) { }
}

使うときは
public void usage()
{
    var instance = new Class(new
    {
        val1 = 1,
        val2 = "a",
        val3 = false,
    });
}

ベースクラスは一度作っておけば後は楽なのですが C# は一つのクラスしか継承できないので このクラスを継承すると他のクラスを継承できなくなるのが辛いところ

あと 使うときが 1 つめよりちょっとだけ (new {}) 長くなっています

また 変数名と値の型のどちらも補完が効かずに入力が面倒です
変数名間違ってても実行するまでわからないですしここは困るかも

でも 最初の補完いるときはオブジェクト初期化子で書いて あとでこの形に修正すれば書いてるときに補完は効くからありかも


ところで $"<{name}>k__BackingField" がなんなのかはこっちの記事