【C#】readonly structの値をメンバーメソッドを呼んで変更しているように見せる方法

C#
このエントリーをはてなブックマークに追加

C#のreadonly structはフィールドすべてにreadonly制約を掛けることによって防衛的コピーを防げる素晴らしい機能ですが、その反面readonlyなため値の書き換えができなくなります。 (※C#コードコンパイル時の話、リフレクション先輩のことは考えないでください)

    readonly struct ReadOnlyValue<T>
    {
        public readonly T Value;

        public ReadOnlyValue(T value)
        {
            Value = value;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var value = new ReadOnlyValue<int>(1);
            Console.WriteLine(value.Value); // 1
            value.Method();
            // たとえばここで10を出力させたいとき1つ上の行のvalue.Method();のMethodはどう定義したらいい?
            Console.WriteLine(value.Value); 
        }
    }

たとえば、このようなコードがある場合readonly structなのでメソッドを呼ぶだけでは値は変わらないはずです。(防衛的コピーも起きない)

しかし、値を変える方法は存在します。さぁどうやったら変えれるでしょうか?っていう話です。


まぁ、焦らさずに答えを出します。

    static class ReadOnlyValueExtension
    {
        public static void Method(ref this ReadOnlyValue<int> value)
        {
            value = new ReadOnlyValue<int>(10);
        }
    }

このような拡張メソッドを定義してやるだけでいいのです。 インスタンスメソッドでは値を変えることはできません。

参照渡しの拡張メソッドですが、見たことない方もいるかもしれません。それもそのはずreadonly structと同じくC# 7.2で追加された機能です。

また、参照渡しの拡張メソッドですが、通常の参照渡しと違って構造体にしか定義できなかったりといろいろと制限があります。 (参照型も許すとrefを明示することなく参照を書き換えれてしまうという害悪プレイができるようになるため当然と言えば当然ですね)


さて、この手法ですが使いどころがあるのか?と言われるといまいち効果的な使いどころが思いつきませんでした。 readonly ref structにして参照渡しをしまくれば構造体の無駄なコピー絶対許さないコードができあがり、この手法を使うことで防衛的コピーのようなことが手動で再現できますが、値の変更があるなら普通のstructやclassにしてしまえばいいじゃんということに帰結して、使いどころが思いつきませんでした。

よって、この話は小ネタ程度に小耳に挟んでいただけれたらなぁ…と