試用 ModVB(一):安裝教學+使用 JSON 常數和 JSON 模式匹配

2022-08-28 21:00:32

 

前排提醒:閱讀此文章並充分嘗試 ModVB 的新語法需要較長的時間。對於程式設計師而言,如果你工作時不用 VB,則最好避免在上班時間看,以免被領導認為你在長時間摸魚。

什麼是 ModVB

ModVB 是一個免費和獨立的 VB.NET 「mod」,裝一個 VSIX 和對應的 NuGet 包就能用新功能。

其中 mod 的意思包含:修改版(modified),現代(modern),以及 取模運算(modulo) (VB + ModVB) Mod VB = ModVB

 

關於 ModVB 的作者

Anthony D. Green 在微軟的託管程式語言團隊有八年工作經驗。大多時候是 Roslyn 編譯器團隊的專案群 / 專案集經理(Program Manager)。他從 13 歲起從 VB4 開始程式設計,在釋出 ModVB 的第一個公測版本時 37 歲。

 

安裝過程

注:編寫此文章時,Visual Studio 2022 版本為 17.3.2,外掛版本為 0.0.1.0。範例中使用的作業系統是 Windows 10 x64 專業版 21H2,簡體中文。

建立 Visual Studio 2022 的獨立環境

此步驟用於隔離裝了 ModVB 的環境和沒裝的環境,以便外掛的分別管理。

  • 啟動 Visual Studio 2022 開發人員命令提示字元
  • 執行命令 devenv /rootSuffix ModVB
  • 建議為 ModVB 建立一個快捷方式,例如

"C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\devenv.exe" /rootSuffix ModVB

新增 ModVB 的 MyGet VSIX 源

從選單上開啟 工具→選項,左側找到 環境→擴充套件

新增其他擴充套件庫:

  • 名稱: ModVB
  • 連結: https://www.myget.org/F/modvb/vsix/

點選應用(Apply)

新增 ModVB 的 NuGet 源

還是在選項對話方塊裡。左側找到NuGet包管理器→程式包源

在右邊新增新的包源並且確保它已經打勾

  • 名稱: ModVB
  • 源: https://www.myget.org/F/modvb/api/v3/index.json

點更新,然後確定。

下載和安裝 ModVB 語言服務拓展

  • 從選單選擇 擴充套件→管理擴充套件
  • 在左側選擇 聯機→ModVB
  • 下載並安裝 「ModVB Language Services (Built with Roslyn)」
  • 重啟 Visual Studio 完成拓展的安裝

如何使用ModVB

注:由於程式碼高亮不完全支援這種修改過的 VB,對於未提及執行結果的程式碼,使用截圖表示。僅對於程式碼高亮沒問題的程式碼和帶有執行結果的程式碼使用文字表示。

如何:啟用ModVB編譯器

  • 新建一個 VB 專案,這裡以 .NET 6.0 控制檯應用為例。
  • 在專案的參照上右鍵,選擇管理 NuGet 包
  • 右上方的下拉框裡面,包源選擇 ModVB
  • 左邊勾選包含預釋出版
  • 安裝 「ModVB.Compilers.Toolset」。這個包是 ModVB 編譯器。

如果下載速度慢,請自備對MyGet有效的加速器,或者找已經下載了並且你相信的人分享。

第一批功能:JSON 常數和模式匹配

選擇JSON變數的型別

安裝對應的 NuGet 包

使用 JSON 相關語法之前首先需要選擇 JSON 常數使用哪些庫。目前可選的有 Newtonsoft.Json 和 System.Text.Json。

如果選擇 Newtonsoft.Json,則需要安裝

如果選擇 System.Text.Json,則需要安裝

預設情況下,JSON 常數和模式匹配使用 Newtonsoft.Json 定義的型別。如果選擇它之外的庫,則需要定義編譯常數來更改預設型別。

指定自動推斷的 JSON 型別

假設有個專案,上述兩種庫都安裝了。預設情況下,Json 物件是  Newtonsoft.Json.Linq.JObject。

如果想要預設用 System.Text.Json,需要在專案檔案中定義編譯常數。

<PropertyGroup>
  <DefineConstants>DEFAULT_JSON_OBJECT_TYPE = "System.Text.Json.Nodes.JsonObject",DEFAULT_JSON_ARRAY_TYPE = "System.Text.Json.Nodes.JsonArray"</DefineConstants>
</PropertyGroup>

效果如下圖所示

按JSON變數的型別構造

那麼,如果一個專案裡想同時使用兩種JSON型別呢?也是能做到的。只不過你需要寫上JSON變數的型別,而不是讓編譯器自動推斷型別。

JSON 常數

就像變數可以用 XML 常數初始化一樣,ModVB 支援用 JSON 常數初始化變數,並且用法與 XML 常數有一定的相似性。

此章節的範例使用 Newtonsoft.Json 作為預設 JSON 型別。

以 NuGet 工具版本查詢結果的片段為例。請求 https://dist.nuget.org/tools.json 能夠得到類似於這樣的 JSON並儲存在變數中:

假如你打算生成類似的 JSON,可以把別的變數直接寫進去換掉JSON的值。

有更多的版本需要顯示也是沒問題的,可以用 LINQ 表示式做個 JSON 物件陣列出來,然後放到總的 JSON 裡面。具體做法可參照下面的範例。

範例

下列程式碼展示如何用 LINQ 結合 JSON 常數語法拼一個新的 JSON 出來。

 

ModVB

Module Program

    Sub Main()
        Dim latestVersion = "6.3.0"
        Dim stableVersions = {
            (ver:="6.2.1", uploaded:="2022-06-14T17:00:00.0000000Z"),
            (ver:="6.2.0", uploaded:="2022-05-10T13:00:00.0000000Z"),
            (ver:="6.1.0", uploaded:="2022-02-15T13:00:00.0000000Z")
        }

        Dim nugetVersions = {
  "nuget.exe": [
    {
      "version": latestVersion,
      "url": $"https://dist.nuget.org/win-x86-commandline/v{latestVersion}/nuget.exe",
      "stage": "Released",
      "uploaded": "2022-08-09T13:00:00.0000000Z"
    },
    From verInfo In stableVersions
    Select {
      "version": verInfo.ver,
      "url": $"https://dist.nuget.org/win-x86-commandline/v{verInfo.ver}/nuget.exe",
      "stage": "ReleasedAndBlessed",
      "uploaded": verInfo.uploaded
    }
  ]
}

        Console.WriteLine(nugetVersions)
    End Sub
End Module

 

這段程式碼的執行結果是:

 

JSON

{
  "nuget.exe": [
    {
      "version": "6.3.0",
      "url": "https://dist.nuget.org/win-x86-commandline/v6.3.0/nuget.exe",
      "stage": "Released",
      "uploaded": "2022-08-09T13:00:00.0000000Z"
    },
    {
      "version": "6.2.1",
      "url": "https://dist.nuget.org/win-x86-commandline/v6.2.1/nuget.exe",
      "stage": "ReleasedAndBlessed",
      "uploaded": "2022-06-14T17:00:00.0000000Z"
    },
    {
      "version": "6.2.0",
      "url": "https://dist.nuget.org/win-x86-commandline/v6.2.0/nuget.exe",
      "stage": "ReleasedAndBlessed",
      "uploaded": "2022-05-10T13:00:00.0000000Z"
    },
    {
      "version": "6.1.0",
      "url": "https://dist.nuget.org/win-x86-commandline/v6.1.0/nuget.exe",
      "stage": "ReleasedAndBlessed",
      "uploaded": "2022-02-15T13:00:00.0000000Z"
    }
  ]
}

 

上面的例子如果你覺得 nugetVersions 的變數宣告太長了,你可以提取變數出來。JSON 常數支援扁平化操作,能夠用來將多個 JSON 物件合併成一個,而不導致物件層次加深。

下面的程式碼將第一個版本資訊中的兩個屬性提取了變數出來,並且剩餘版本資訊提取出了另一個變數。程式的執行結果不變。

 

ModVB

Module Program

    Sub Main()
        Dim latestVersion = "6.3.0"

        Dim stableVersions = {
            (ver:="6.2.1", uploaded:="2022-06-14T17:00:00.0000000Z"),
            (ver:="6.2.0", uploaded:="2022-05-10T13:00:00.0000000Z"),
            (ver:="6.1.0", uploaded:="2022-02-15T13:00:00.0000000Z")
        }

        Dim stable = From verInfo In stableVersions
                     Select {
      "version": verInfo.ver,
      "url": $"https://dist.nuget.org/win-x86-commandline/v{verInfo.ver}/nuget.exe",
      "stage": "ReleasedAndBlessed",
      "uploaded": verInfo.uploaded
    }

        Dim releasedStage = {
            "stage": "Released",
            "uploaded": "2022-08-09T13:00:00.0000000Z"
        }

        Dim nugetVersions = {
  "nuget.exe": [
    {
      "version": latestVersion,
      "url": $"https://dist.nuget.org/win-x86-commandline/v{latestVersion}/nuget.exe",
      {releasedStage}
    },
    stable
  ]
}

        Console.WriteLine(nugetVersions)
    End Sub
End Module

 

JSON 模式匹配

JSON模式匹配能在判斷JSON的結構和內容的同時將所需的資訊提取成變數。

例子

以 JSON 常數例子中的 JSON 為例。假設你需要取得所有 stageReleasedAndBlessednuget.exe 版本以及其它資訊,可以用下面的程式碼:

 

ModVB

Console.WriteLine("所有已釋出的穩定版:")

Select Case ShapeOf nugetVersions
    Case {"nuget.exe": nodes}
        For Each node In nodes
            Select Case ShapeOf node
                Case {
                  "version": ver As Version,
                  "url": url As String,
                  "stage": "ReleasedAndBlessed",
                  "uploaded": relaseDate As Date
                }

                    Console.WriteLine($"{ver} 釋出於 {relaseDate:F},下載地址是 {url}")
            End Select
        Next

End Select

 

程式碼產生以下輸出

 

控制檯

所有已釋出的穩定版:
6.2.1 釋出於 2022年6月15日 1:00:00,下載地址是 https://dist.nuget.org/win-x86-commandline/v6.2.1/nuget.exe
6.2.0 釋出於 2022年5月10日 21:00:00,下載地址是 https://dist.nuget.org/win-x86-commandline/v6.2.0/nuget.exe
6.1.0 釋出於 2022年2月15日 21:00:00,下載地址是 https://dist.nuget.org/win-x86-commandline/v6.1.0/nuget.exe

 

Select Case ShapeOf 語句

Select Case ShapeOf 是模式匹配專用的 Select Case。它會改變Select CaseCase的含義,讓它進行模式匹配,而不會與常規的 Select Case 語法產生衝突。

匹配 JSON 物件

匹配 JSON 物件需要用一對大括號包含 JSON 匹配模式,以下圖為例解釋。

  • 匹配的順序是從上到下,在行內從左到右。圖中的屬性1先匹配,屬性2後匹配。
  • 想要排除一個屬性,可以匹配一個屬性值是空。圖中要求屬性3必須為空。這樣JSON沒寫屬性3或者屬性3寫了是空才能匹配上。
  • 如果冒號左邊寫問號,那麼屬性為空的時候仍然會匹配上。圖中的屬性4是可以為空的。包括這個屬性沒寫,或者屬性值寫了是空。
  • 如果 JSON 中有別的屬性,並且大括號裡面寫了屬性的匹配規則,那麼其餘的屬性不影響匹配結果。
  • 如果大括號裡面也可以什麼都不寫,這個時候就只能匹配空的 JSON 物件。

匹配 JSON 陣列

匹配 JSON 陣列需要用一對中括號包含 JSON 匹配模式,以下圖為例解釋。

  • 上圖的意思是,按順序從頭開始匹配陣列元素,它至少有指定的這些元素:"元素1", "元素2", 345, true, false, null 。如果陣列比預期的長,匹配仍然能夠成功。
  • 如果中括號裡面什麼也不寫,則只能匹配空陣列。

匹配 JSON 常數

任何 VB 常數表示式在這裡都能使用。包括 Date, Decimal, Nothing。JSON 的常數(true, false, null)也能用。

按型別匹配並定義變數

  • 如果值不為空,而且可轉換為指定的型別,則匹配。
  • 轉換考慮了 TryCast, VB 內建的轉換規則 以及 TryParse 之類的其它轉換方式。
  • 圖中屬性1會按字串解析 JSON 內容並且嘗試 TryParse
  • 圖中屬性2會按字串解析 JSON。
  • 圖中屬性3會原樣返回 JSON 的值,前提是屬性3不是空。

 

用問號表示可選

基本上每個模式匹配表示式最後都可以寫一個問號,讓表示式也能匹配空。這裡認為屬性未定義和值為空是相同的情況。下圖是一些例子。

匹配一個可以為空的物件

匹配一個可以為空的值

屬性1可以為空,屬性2匹配一個屬性並定義變數,屬性值可以為空

當屬性值為空時,定義的變數具備它的預設值。

執行時拓展和 API 相容性

JSON 常數和模式匹配是基於模式的,類似於 LINQ 和 For Each 迴圈。它並不會綁在特定的庫和型別上。這些功能在分析或編譯程式碼時從特定的名稱空間查詢符合條件的型別,使用其中的成員。你甚至可以只裝包含 ModVB 編譯器的包,具體的模式匹配拓展包由你自己編寫。通過反編譯你的使用 JSON 常數和模式匹配的專案,你可以瞭解到如何編寫 JSON 常數拓展包和模式匹配拓展包。

參考資料

ModVB 作者寫的兩篇介紹:

版權資訊

此文章使用 CC BY-SA 4.0 協定,範例程式碼使用 MIT 協定。

此文章目前計劃在 部落格園,GitHub,嗶哩嗶哩,百度貼吧 使用使用者名稱為 Nukepayload2 的賬號釋出。如果文章在別的地方出現,或者使用者名稱不是 Nukepayload2,則屬於被轉載。