【VB.NET】Record型を使ってみる

# はじめに
これは、[Visual Basic Advent Calendar 2022](https://qiita.com/advent-calendar/2022/vb)の7日目の記事となります。

VB.NETは、Microsoftの方針により新機能の対応はしなくなりました。そのため、C#9.0から追加されたレコード型は使用できません。
しかし、[Mohammad Hamdy Ghanem(VBAndCs)](https://github.com/VBAndCs)さんが、2020年11月頃に代替案を提案してくれました。

# レコード型
[C# 9.0 の新機能](https://learn.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-9)

その中の機能の一つとして、新たな型「レコード（record）」が追加されました。
「レコード」の概要は以下のように説明されています。

>C# 9.0 には "レコード型" が導入されています。これは、等価性の値のセマンティクスを提供するための合成されたメソッドを提供する参照型です。 既定では、レコードは変更できません。

# VB.NET対応経緯
2020年11月頃にレコード型の代替案を提案されました。

https://github.com/dotnet/vblang/issues/584

この時はWinForm上で、レコード型を入力したものを代替クラスに変換するジェネレーターでした。

https://github.com/VBAndCs/VB-Record-Generator

## ソースジェネレータ対応版
その後、.NET SDK 5.0 から対応したに"ソースジェネレータ (Source Generators)" という機能を使用したものになりました。
"ソースジェネレータ" とは何かというと、いうなれば、.NET系開発言語のコンパイラに対するある種の"アドイン"を、アプリケーションプログラマが実装・配付できるようになる機能です。
ビルド時に追加の対象の開発言語のソースコードを動的に作成し、対象プロジェクトに付け加えるようになります。

https://github.com/VBAndCs/VB-Record-Source-Generator

# 実装
## 準備
~~NuGetで「VB-Record-Source-Generator」をインストールします。~~
NuGetで「RecGen」をインストールします。名前が変更になった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/f7b91cb5-58d7-868a-6f04-33b3051f8951.png)

## レコード型を追加
プロジェクトにテキストを追加します。拡張子は「rec」を指定します。　
今回は例として「PersonRecord.rec」とします。
recordキーワードを使用できます。

```vb:PersonRecord.rec
Public Record PersonRecord (
    FirstName As String = "",
    LastName As String = ""
)
```

記述後に「PersonRecord.rec」ファイルのプロパティでビルドアクションを「VB　アナライザー追加ファイル」に変更します。
保存すれば、自動的にソースコードを動的に作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/72939716-4847-7234-8ce2-6fb6a210b798.png)

### シンタックスハイライト
拡張子「rec」のままだとシンタックスハイライトが効かないので、Visual　Studioのツールメニューのオプションから、テキストエディターのファイル拡張子を選択します。拡張子に「rec」を指定してエディターに「Micorosoft Visual Basic」を指定して追加(適用)ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/8d62b9fd-ca8a-e013-ee7a-090bc45d8bdd.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/1fb34b3e-d988-b2dd-841f-0fe03e4269d7.png)

## 確認
PersonRecordが使えるようになっています。
また、ソースジェネレータを使用するので毎回ビルドするといいでしょう。

参考にした記事

https://qiita.com/shimamura_io/items/80982b11ce41eca03e10

```vb:PersonClass.vb
Public Class PersonClass
    Public ReadOnly Property FirstName
    Public ReadOnly Property LastName

    Public Sub New(ByVal first As String, ByVal last As String)
        FirstName = first
        LastName = last
    End Sub
End Class
```

```vb:Program.vb
Module Program
    Sub Main(args As String())

        Dim personClass1 As PersonClass = New PersonClass("Bill", "Wanger")
        Dim personClass2 As PersonClass = New PersonClass("Bill", "Wanger")

        Console.WriteLine(personClass1.Equals(personClass2))
        Console.WriteLine(personClass1.ToString())

        Dim personRecord1 As PersonRecord = New PersonRecord("Bill", "Wanger")
        Dim personRecord2 As PersonRecord = New PersonRecord("Bill", "Wanger")

        Console.WriteLine(personRecord1.Equals(personRecord2))
        Console.WriteLine(personRecord1.ToString())

        Console.ReadKey()

    End Sub
End Module
```

### 結果
```vb:実行結果
false        ' PersonClass同士のEqualsの結果
PersonClass  ' PersonClassのToString
true         ' PersonRecord同士のEqualsの結果
PersonRecord { FirstName = Bill, LastName = Wagner }  ' PersonRecordのToString
```

>オブジェクトが同一 = インスタンスが同じかどうかを比較するだけ、ということですね。
personClass1とpersonClass2はそれぞれnewされていて別のインスタンスなので、結果はfalseということです。
一方、PersonRecord同士は別のインスタンスとなっているにも関わらずEqualsの結果はtrueとなっています。
(中略)
また、classではToStringも既定ではそのclassの名称を出力するだけですが、recordではToStringもOverrideされてプロパティ名、その中の値も出力してくれるようになっています。
[【C# 9.0 新機能】レコード（record）型はクラスと比べて何がオトクなのか](https://qiita.com/shimamura_io/items/80982b11ce41eca03e10#class-%E3%81%A8-record-%E3%81%AE%E5%8B%95%E4%BD%9C%E6%AF%94%E8%BC%83)

## 応用
応用として下記サイトで、Dictionaryのキーとして使用できると思ったんですよね。
ただ同じキーなら本来エラーか上書きされると思うのに、そのまま追加されてしまうので現在確認中です。
また追記します。

https://note.com/potato_d/n/n99cf0d9edb23

https://takap-tech.com/entry/2018/05/26/235404

【2022/12/14追記】
Issueを書いて修正してもらいました。どうせなので別記事にします。

https://qiita.com/yaju/items/50609344a9d18a67e1a3

# 最後に
VB.NETでもレコード型が使用できることを確認できました。
ソースジェネレーターという機能自体は耳にはしていたが、自分では使い道が思いつかなかったですが、今回こういった形で使えることが分かっただけでも良かったです。