Visual Basicで「.NET 5.0 で使える C# 9.0の新機能」について調べてみた

# TL;DR

- [C# 小ネタ：.NET 5.0 で使える C# 9.0 で気に入ってる機能紹介](https://qiita.com/okazuki/items/8043ea6f0095b3019cdf)について、Visual Basicでの状況について調査してみました。

    [.NET 5.0で互換性を考慮したサポートは発表されています](https://qiita.com/tfukumori/items/0c0702b8c542c0ab054e)が、把握している限り言語的な更新は入っていません。

# 環境

- Visual Studio 2019 Version 16.7.6

# レコード型

サポートされていません。

ただし、
https://qiita.com/yaju/items/b1c58162eb634fd58619
によると、直近では、Visual Basicでレコード型を実装する試みがされているそうです。
[I hate C# records. Please don't ever bring them to VB!](https://github.com/dotnet/vblang/issues/584)

# トップレベルステートメント

サポートされていません。

# null じゃないんですよを簡単に書ける

`IsNot`を使います。

```vb
' 不等号を使う
If (o <> Nothing) Then
    ' Nothing じゃない
End If

' IsNotを使う
If o IsNot Nothing Then
    ' Nothing じゃない
End If
```

`Is` と `Not`の間に空白を入れるとエラーとなります。
関連する議論: https://github.com/dotnet/vblang/issues/581

```vb
' エラーになります
If o Is Not Nothing Then
    ' Nothing じゃない
End If
```

**参照記事のコメントに記載されていたObject.ReferenceEqualsについて**

Object.ReferenceEqualsに、IDE0041を示してきました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204150/80ace852-db1f-f8ca-6562-e4e12e6b661c.png)

こちらを、.NET Core 3.1を利用して逆アセンブルしてみました。.NET 5.0での検証は環境がないため未実施です。

生成されるMSILは異なります。C#での例を考えると同じことを表現している気もします。


<details><summary>ソースコードとMSIL（逆アセンブル）の結果</summary><div>

**VB.NET**

```vb
Sub Main()
    Dim o = New Sample

    If o Is Nothing Then

    End If

    If Object.ReferenceEquals(o, Nothing) Then

    End If
End Sub
```
**MSIL**

```
.method public static void  Main(string[] args) cil managed
{
  .entrypoint
  .custom instance void [System.Runtime]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) 
  // コード サイズ       31 (0x1f)
  .maxstack  2
  .locals init (class ConsoleApp2.Sample V_0,
           bool V_1,
           bool V_2)
  IL_0000:  nop
  IL_0001:  newobj     instance void ConsoleApp2.Sample::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldnull
  IL_0009:  ceq
  IL_000b:  stloc.1
  IL_000c:  ldloc.1
  IL_000d:  brfalse.s  IL_0010
  IL_000f:  nop
  IL_0010:  nop
  IL_0011:  ldloc.0
  IL_0012:  ldnull
  IL_0013:  call       bool [System.Runtime]System.Object::ReferenceEquals(object,
                                                                           object)
  IL_0018:  stloc.2
  IL_0019:  ldloc.2
  IL_001a:  brfalse.s  IL_001d
  IL_001c:  nop
  IL_001d:  nop
  IL_001e:  ret
} // end of method Program::Main
```

[OpCodes.Ceq フィールド (System.Reflection.Emit) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.reflection.emit.opcodes.ceq?view=netcore-3.1)

</div></details>

# 大なり小なりの比較も簡単に

Ifでは使えず、同様のことを行うにはSelect句を使用します。
💡 Ifの中でAnd Orで表現できたほうがわかりやすい気がします。

参考：
https://docs.microsoft.com/ja-jp/dotnet/visual-basic/language-reference/statements/select-case-statement

`If`では使えません。

```vb
Dim x = 15

'エラーになります
If (x Is >= 10 And <= 20) Then
    Console.WriteLine("10 以上で20 以下")
End If

'エラーになります
If (x Is >= 10 And <= 20 Or >= 100) Then
    Console.WriteLine("10 以上で20 以下もしくは 100 以上")
End If
```

`Select Case`では以下の記載となります。

```vb
Dim x = 15

Select Case x
    Case 10 To 20
        Console.WriteLine("10 以上で20 以下")
End Select

'1行で記載した場合
Select Case x : Case 10 To 20
    Console.WriteLine("10 以上で20 以下")
End Select

Select Case x : Case 10 To 20, >= 100
    Console.WriteLine("10 以上で20 以下もしくは 100 以上")
End Select
```

# Target-typed new 式

サポートされていません。

# まとめ

やはり、言語仕様の更新が頻繁に行われているというのはうらやましいものがあります。

特に、Record型はうらやましいですね。

「null じゃないんですよを簡単に書ける」や「大なり小なりの比較も簡単に」などの記法は、逆に影響を与えている面もあるかもしれません。C# に移行するとswitchの記法は少し面倒に感じましたので。

参考までに、Visual Basicの言語仕様に関する議論はここで行われています。
https://github.com/dotnet/vblang/issues