コンウェイとピーターの狭間で

ずっと放置しつつQiitaに移ったりしていましたが、スマートホームについて書いていこうかと

頑固な方がモテるのか?

最近、状態を持たない設計手法をよく耳にします。

  • 関数型言語
  • DDDのバリューオブジェクト
  • .NETのImmutableCollection

これからの時代は不変な型、Immutable型が重要なのでしょう。

ようするに、態度がコロコロ変わる男はモテなくて、一度決めたら突っ走れってことですね。

ということで、我らのC#さんでImmutable型を作るにはどうすればいいのか考えてみます。

public get/private setなプロパティがありフィールドを変更するメソッドがないのクラス

ようするにこんな型です。

public class FakeImmutableType
{
    public FakeImmutableType(int no)
    {
        No = no;
    }
    public int No{ get; private set; }
}

コンストラクタのみでフィールドは初期化され、それ以降はgetのみ可能です。 もちろん、フィールド/プロパティの型もImmutableである必要があります。 (なので、配列はアウト)

この方法のメリットは、後述するパターンに比べコード量が少ないこと。

すべてのフィールドがreadonlyな型

こんな型です。

public class ImmutableType
{
    private readonly int _no;

    public ImmutableType(int no)
    {
        _no = no;
    }

    public int No
    {
        get { return _no; }
    }
}

前者のパターンと比べるとコード量は増えますが、 言語仕様としてコンストラクタ以外での書き換えを禁止していますから、 クラスがImmutable型であることを明確に表現できます。

また、うっかりフィールドを書き換えるメソッドを作る心配がありません。

ただし、フィールド型がImmutableであることという前提は変わりません。

あと、次のC# 6.0では次のように書けるように簡素に書けるそうです。

public class ImmutableType
{
    public ImmutableType(int no)
    {
        No = no;
    }

    public int No { get; }
}

Immutableな型であることを宣言する

2つのパターンをあげましたが、どちらの場合もフィールド型がImmutableである必要があります。 注意すればいいだけなのですが、これから使おうとする型がImmutableな型なのかどうかが知りたくなります。 特に標準クラスは何となくわかりますが、独自クラスの場合は目印が欲しくなります。

私がよく使う手はこんな感じで、[Immutable]属性を作って表現します。

public class ImmubtaleAttribute : Attribute
{
}

[Immutable]
public class ImmutableType
{

Visual Studioのインテリセンスには出せませんが、定義を参照すればすぐにわかります。

Immutableであることをチェックする

さてさて、Immutableであることを宣言すると、今度は本当にそうなのかチェックしたくなります。 誤ってImmutableでない型をフィールドにしてしまったり、public setなプロパティを作ってしまったり、が起こりえます。 1人で開発するなら気を付けるだけですが、チーム開発となるとそうもいきません。

「すべてのフィールドがreadonlyな型」のパターンならチェックはこんな感じですね。

  • [Immutable]属性がつけられている型について以下の制約を満たすこと
    • すべてのフィールドがreadonlyである
    • すべてのフィールド型がImmutable型である

完璧には無理ですが、Immutable型のホワイトリストを持てば、リフレクションで割と簡単に実現できそうです。 ちょいと作ってみようと思います。

ただ、もう1つのパターンは、「メソッドの中でフィールドを書き換えていないこと」という制約が必要ですので これはちょっと難しいですね。 out, refあたりを無視すれば、Roslynを使ってできるかもしれません。 気が向けば作ってみます。