LoginSignup
2
0

More than 3 years have passed since last update.

C# 9.0 新機能 「レコード(record)型 / with 式」

Last updated at Posted at 2021-03-25

今更ながら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 }
    }
}

以上です。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0