今更ながらC#9.0から追加されたレコード型とwith
式を使ってみたのでメモ
https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/tutorials/records
前提
例えば、平面上の座標を示すPoint2D
クラスのインスタンスが複数存在する時に、
あるインスタンスが他のインスタンスと内容(X座標とY座標の値)が等しいかを判定したいとします。
public class Point2D
{
public int X { get; }
public int Y { get; }
public Point2D(int x, int y) => (X, Y) = (x, y);
}
public class Program
{
private static void Main(string[] args)
{
var src = new Point2D(10, 20);
var p1 = new Point2D(5, 10);
var p2 = new Point2D(10, 20);
//Point2D のインスタンス src が 他のインスタンス p1,p2 と
//内容(X座標とY座標の値)が等しいかを判定したい
}
}
レコードを利用しない場合(~C#8.0)
参照先ではなく、内容が等しいかを判定するためには
C#8.0まではPoint2D
クラスでEquals
メソッドとGetHashCode
メソッドをオーバーライドする必要がありました。
public class Point2D
{
public int X { get; }
public int Y { get; }
public Point2D(int x, int y) => (X, Y) = (x, y);
//X座標とY座標の値で等価比較をするようにオーバーライド
public override bool Equals(object obj)
{
if (obj == null || obj.GetType() != GetType()) return false;
var p = (Point2D)obj;
return ReferenceEquals(this, p) ||
X == p.X && Y == p.Y;
}
//X座標とY座標の値からハッシュ値を生成するようにオーバーライド
public override int GetHashCode() => HashCode.Combine(X, Y);
}
public class Program
{
private static void Main(string[] args)
{
var src = new Point2D(10, 20);
var p1 = new Point2D(5, 10);
var p2 = new Point2D(10, 20);
//Point2D のインスタンス src が 他のインスタンス p1,p2 と
//内容(X座標とY座標の値)が等しいかを判定したい
Console.WriteLine($"a.Equals(p1):{src.Equals(p1)}"); // False
Console.WriteLine($"a.Equals(p2):{src.Equals(p2)}"); // True
}
}
レコードを利用した場合(C#9.0~)
C#9.0からはレコード型を利用することによって、より簡単に実装することができます。
レコードを定義するには、class
の代わりにrecord
キーワードを使用して型を宣言します。
public record Point2D(int X, int Y);
レコードは、クラスと同じように扱うことができます。もちろんメンバを定義することもできます。
public record Point2D(int X, int Y)
{
public int Property { get; }
public Point2D(int x, int y, int property)
: this(x, y)
{
Property = property;
}
public void Method()
{
Console.WriteLine("foo");
}
}
そして、主に以下の作業を内部的に実施してくれます。
- 型名に続く()で渡した内容で、
get/init
なプロパティを生成public int X { get; init; }
public int Y { get; init; }
- 各プロパティの値を引数に取るコンストラクタの生成
-
Equals
メソッドをオーバーライド- 各プロパティの値(ここではX座標とY座標の値)で等価比較をする
-
GetHashCode
メソッドをオーバーライド- 各プロパティの値からハッシュ値を生成する
-
ToString
メソッドをオーバーライド-
レコード名 { プロパティ名 = 値, ... }
の形式で文字列化する
-
結果として、あるインスタンスが他のインスタンスと内容が等しいかを判定したい時にレコード型を利用すると、今までと比べて簡単に実装することが可能です。
public record Point2D(int X, int Y);
public class Program
{
private static void Main(string[] args)
{
var src = new Point2D(10, 20);
var p1 = new Point2D(5, 10);
var p2 = new Point2D(10, 20);
//Point2D のインスタンス src が 他のインスタンス p1,p2 と
//内容(X座標とY座標の値)が等しいかを判定したい
Console.WriteLine($"a.Equals(p1):{src.Equals(p1)}"); // False
Console.WriteLine($"a.Equals(p2):{src.Equals(p2)}"); // True
//ToStringもオーバーライドされている
Console.WriteLine(src); //Point2D { X = 10, Y = 20 }
}
}
レコードの複製(with 式)
レコード型と同じくC#9.0から追加されたwith
式を使うと、簡単にレコードの複製をすることができます。
コピー先 = コピー元 with { プロパティ名 = 値, ... }
public record Point2D(int X, int Y);
public class Program
{
private static void Main(string[] args)
{
var src = new Point2D(10, 20);
var copy = src with { X = 30, Y = 60 };
Console.WriteLine(copy); // Point2D { X = 30, Y = 60 }
Console.WriteLine(src); // Point2D { X = 10, Y = 20 }
}
}
以上です。