Skip to content

Instantly share code, notes, and snippets.

@ufcpp
Created February 11, 2015 05:20
Show Gist options
  • Save ufcpp/ea855492e206c300ec6d to your computer and use it in GitHub Desktop.
Save ufcpp/ea855492e206c300ec6d to your computer and use it in GitHub Desktop.
値セマンティクスを持つクラス。タプルとレコード再考
// Point の実際の値を持つ構造体。
// 現状の C# だと自分で実際にこういう型定義を書く必要があるけども、タプルにできるのでは。
struct PointValue
{
public int X;
public int Y;
}
// 値セマンティクスを持つ immutable クラス
// レコード型の実装はこうあるべきだろうという内容。
// ここでいう値セマンティクス(value semantics)は、全メンバー比較で Equals、全メンバーのハッシュ値から GetHashCode 計算できるもののこと。
class Point
{
// 唯一の構造体メンバーに readonly を付けているので immutable。
// タプルが入るのであれば、public readonly (int X, int Y) Value; でいいはず。
// Point p に対して (var x, var y) = p; で、(var x, var y) = p.Value; の意味に解釈するのもよさそう。
public readonly PointValue Value;
// 外から mutable な型で値をもらう。いわゆる「ビルダー パターン」。
// PointValue をタプルにするのなら、スプラッティングが効いて、new Point(x, y) とも書けるはず。
// 簡易構文として、new Point { X = 1, Y = 2 } で、new Point(new PointValue { X = 1, Y = 2 }) の意味に解釈するのもよさそう。
public Point(PointValue value) { Value = value; }
// Value に処理を丸投げ
public int X => Value.X;
public int Y => Value.Y;
public override int GetHashCode() => Value.GetHashCode();
public override bool Equals(object obj) => (obj as Point)?.Value.Equals(Value) ?? false;
// 値書き換え(immutable なので別のインスタンスを作る)用の "wither" (GetX, SetX みたいなのを getter/setter というように、WithX をこう言う)
// p.Value { X = 1 } みたいに、new するとき以外でもオブジェクト初期化子を使えると、wither 書くのが楽になるはず。
// Point WithX(int x) => new Point(Value { X = x });
// あと、var q = p { X = 1 }; みたいなので new Point(Value { X = 1 }) の意味に解釈するのもよさそう。
// これなら、自前の wither 定義要らない。
public Point WithX(int x) { var v = Value; v.X = x; return new Point(v); }
public Point WithY(int y) { var v = Value; v.Y = y; return new Point(v); }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment