Visual Basic .NET

Visual Basic 14 における強化点トップ 14

Lucian Wischik

Visual Basic の最新バージョン Visual Basic 14 は、Visual Studio 2015 に同梱してリリースされる予定です。これまでのバージョンは実際には C++ で記述されていましたが、このバージョンは約 1300 万行の VB コードでゼロから書き直されています。開発チームはこの書き直しに際し、VB のすべての部分を徹底的に考え直しました。今回紹介するのは、その中からチーム メンバーが選んだ強化点トップ 14 です。メンバーは、コーディング エクスペリエンス、プロジェクト システム基盤、言語自体からまんべんなく好みの強化点を選んでいます。

コーディング エクスペリエンスの強化

1. リファクタリング: 「リファクタリングを直接製品に組み込めるようになりました」 - .NET MVP Jim Wooley

これまでは、メソッドの抽出や一時変数のインライン処理のように、Visual Basic 向けに重要なリファクタリングを取り込むためには、アドオン製品を購入する必要がありました。C# 向けのリファクタリングがいくつか存在していたため、マイクロソフトは Developer Express とパートナーシップを結び、Visual Studio 2005 からは Visual Basic ユーザーが同社の Refactor! アドインを使えるようにしました。Visual Studio 2015 ではリファクタリングが製品に組み込まれるようになります。リファクタリングを使用するには、識別子をクリックするか、サブ式を強調表示します。次に、Ctrl キーを押しながらピリオド (.) キーを押すか、または右クリックして、[Quick Actions] (クイック操作) をクリックします。すると、電球マークが付いた関連操作のコンテキスト メニューが表示されます (図 1 参照)。

Visual Basic 14 組み込みのリファクタリング
図 1 Visual Basic 14 組み込みのリファクタリング

リファクタリングはコンテキストを認識します。たとえば、"Dim circleArea = Math.PI * radius * radius" の右辺をメソッドに抽出する場合、Visual Basic は抽出先のメソッドの名前として "GetCircleArea" を提案します。さらに名前を変更する場合は、インラインの名前変更モードに切り替わります。インラインの名前変更モードはインテリジェントな機能で、既に使用されている名前を選択すると、名前の競合を検出して警告し、競合を回避します。C# プロジェクトで名前を変更した場合でも、ソリューション全体でこの回避機能が働き、できる限り競合を避けるようにします。

2. アナライザー: 「これは注目に値する機能です。使い方はたくさん思い浮かびます。人間が書いたコードはどんなささいなものでも気になります」 — Windows PowerShell MVP のひと言

アナライザーは、電球マーク、コード操作、エラー波線を把握する手段です。このアナライザーを使って、チーム全体にコード規約を強制することができます。ある問題をデバッグすることになったら、アナライザーをプラグインして、ソリューション全体に共通するコードの欠陥をすばやく見つけることができます。使用しているライブラリの多くも、それぞれ組み込みのアナライザーがあれば、コードを認識するようになります。たとえば、Microsoft Azure Storage ライブラリを使った経験がなく、ベスト プラクティスに関する記事も読んだことがないとします。それでも、このライブラリには API を使用する場合に陥りやすい落とし穴を見つけるアナライザーが組み込まれるようになったので、このライブラリを正しく使用しているという確信をすぐに持てます。まるで自分がコードを入力している後ろで、コード レビューのプロがサポートしてくれているようなものです。

新しい [References] (参照設定) の [Analyzers] (アナライザー) ノード (または NuGet) でアナライザーをプロジェクトに追加します。いったんアナライザーがプロジェクトのコンパイルの一部になると、コード入力中にエラーを示す波線が表示されるときに、アナライザーがライブ状態になります。アナライザーはプロジェクトが Visual Studio またはコマンド ラインでビルドされるときにも実行されます。アナライザーはビルド サーバー上でも実行されます。アナライザーはコンパイラの内部にまで "踏み込み"、プロジェクトのソース コードの構文ツリー、型やメンバーを確認します。開発者は、こうした構文ツリーや型とメンバーのおかげで、開発者自身の専門知識をアナライザーにコード化することが驚くほど簡単なのがわかります。たとえば、メンバーお気に入りのアナライザーは、Async Function ... As Task としなければならないのに Async Sub メソッドを使っている場所を検出して警告を発します。この問題は非同期プログラミングで陥りやすいミスで、気付く人が少ないため、同時実行のバグの検出を難しくしています。そのため、コンパイル時にエラーを検出できるのは歓迎すべきことです。独自のアナライザーを作成することについては、roslyn.codeplex.com (英語) を参考にしてください。

3. カーソルを行から動かす必要がない: 「手間がかからなくなります」 - Visual Basic チームのメンバー Dustin Campbell

VB のユーザーはこれまで長い間、コードをある程度入力したらにカーソルをすばやく上下に動かして、エラーを示す波線が表示されるかどうかを確認してきました。さらに、エラーを修正するコードを書いたら、今度はエラーを示す波線を消すためにカーソルを上下に動かす必要もありました。

このようなムダな操作が必要なくなりました。カーソルをそのままにしておいても、エラーを示す波線が自動的に表示または非表示になります。

4. XML ドキュメント コメントの参照: 「ドキュメントが重要な開発者にとっては、正しい方向への大きな前進です」 - .NET MVP Sam Harwell

XML ドキュメント コメントは重要でしょうか。以下に簡単な例を示します。

''' <summary>
''' Similar to <see cref="List(Of Integer).Count"/>
''' </summary>
''' <param name="e">Thing to count</param>
''' <remarks></remarks>
Sub Count(e As IEnumerable)
End Sub

以前のバージョンの VB では、コメントに cref と param-name を入力すると、入力候補一覧のヘルプは表示されましたが、そこから開発者自身が選択する必要がありました。コンパイラが最低限の検証をいくつか行って、その名前が存在することを確認していましたが、文字の色がグレーだったため見つけにくく、リファクタリングも簡単ではありませんでした。

Visual Basic 14 では cref と param-name のそれぞれの引数が別々に色付けされるようになります。マウスでポイントすればヒントが表示されます。記号名変更リファクタリングを行うと (Ctrl キーを押しながら、R キーを押し、もう一度 Ctrl キーを押しながら、R キーを押します)、Visual Basic が記号への参照の名前をすべて変更しますが、そのとき cref と param-name 内の記号も変更されます。変更された名前の 1 つを右クリックして、定義へ移動 (Go to Definition) したり、すべての参照を検索 (Find All References) することができます。複数のオーバーロードがあるメソッドを参照する場合は、目的のオーバーロードを 1 つだけ厳密に参照することができるようになります。このような変更により、XML ドキュメント コメントでの型参照が簡単になり、確認しやすくなります。

プロジェクト システム基盤

5. ソリューション エクスプローラーの [References] (参照設定) ノード: 「実際には、毎日のように参照設定を微調整しなければなりません」 - Visual Basic Team メンバー Lucian Wischik

図 2 は、一般的な Visual Basic 14 プロジェクトが、ソリューション エクスプローラーに表示されるようすを示しています。

ソリューション エクスプローラーに表示されるようになった [参照設定] ノード
図 2 ソリューション エクスプローラーに表示されるようになった [参照設定] ノード

この [参照設定] ノードが新しくなります。これまではこのノードが表示されていなかったため、[すべてのファイルを表示] をクリックして表示していましたが、その際無関係なファイルも大量に表示されました。

Windows フォーム プロジェクトから始め、通常は参照設定の適切なセットが含まれていた 10 年前であれば、この動作も当然だったかもしれません。しかし、現在の開発では [参照設定] ノードが頻繁に使われます。特に NuGet 参照を管理することが増えています。参照設定がソリューション エクスプローラーで簡単に確認できるのは、小さいながらも便利な変更点です。

6. 共有プロジェクト: 「実際、リンク ファイルよりも優れたツールで共有作業が容易になります」 - Windows Developer MVP Morten Nielsen

2 つ以上のプロジェクトでコードを共有するとします。コードの共有は、Windows Presentation Foundation (WPF) 版と Windows Phone 版のアプリを両方管理する場合など、ごく一般的に行われます。共有の目的は常に変わらず、コードを最大限に再利用することです。たとえば、あるプロジェクトでバグを修正すれば、他のプロジェクトが自動的にその恩恵を受けます。

これまでは 2 つの手法から選択していました。1 つはリンク ファイルを使って共通のソース コードを共有する手法で、もう 1 つは共有コードをポータブル クラス ライブラリに設計し直して、共通バイナリを共有する手法です。Visual Basic 14 では、第 3 の手法として、共有プロジェクトという強力な手法が可能になります。

共有プロジェクトが用意されたのはなぜでしょう。コードを共有するという作業は、優れた "フリーサイズ" のソリューションがなければ非常に困難です。ポータブル クラス ライブラリは適切かつ明確なソリューションですが、共通コードから WPF プロジェクトや Phone プロジェクトを呼び出すことにならないように、コードを設計する必要があります。共通コードから呼び出せるのは WPF と Phone の両方に存在する システム API のみです。共有プロジェクトはこのような再設計の必要がありません。そのため、より使いやすくなっています。

共有プロジェクトをセットアップするには、ソリューションを右クリックし、[Add] (追加) をポイントし、[New Project] (新しいプロジェクトの追加)、[VB] (Visual Basic)、[Shared Project] (共有プロジェクト) の順にクリックします。次に各プロジェクトの [References] (参照設定) ノードを右クリックして、[Add] (参照の追加) をポイントし、[Shared Projects] (共有プロジェクト) をクリックします。共有プロジェクトとは、あるプロジェクトが参照する各プロジェクトにインクルードされる、ソース ファイル、XAML ファイル、画像などのアセットのコレクションにすぎません。

プロジェクトごとに、[My Project] (マイ プロジェクト)、[Compile] (コンパイル)、[Advanced Compile Options] (詳細コンパイル オプション)、[Custom Constants] (カスタム定数) の順にクリックして、WPF や PHONE のようなカスタム定数をセットアップすることもできます。このようにカスタム定数をセットアップしたら、共有コード内で次のようにしてプロジェクト固有の API を呼び出すことができます。

#If WPF Then
  ' nothing needed
#ElseIf PHONE Then
  ShowBatteryStatus()
#End If

7. コンパイル時間を 50% 短縮: 「50% は冗談ではありません」 - .NET MVP Sam Harwell

これまでの Visual Basic コンパイラは C++ で記述していました。Visual Basic 14 では、すべて VB で書き直し、圧倒的に高速化を図っています。以下に 2 つ比較を示します。

  • 大きなソリューションのビルド (1300 万行のコード) - 68 秒から 41 秒へ短縮
  • ソリューションのコールド読み込み (Windows Store アプリ) - 6.7 秒から 4.6 秒へ短縮

これは大幅な時間の節約です。「最後までコーディングしてしまうか」、「F5 キーを押してブレークポイントまで実行してみるか」を悩んで躊躇することがなくなるため、ありがたい改善点です。

C++ の方が VB より高速だと考えている方は、この 50% のパフォーマンス向上に驚くかもしれません。VB では、アルゴリズム、データ構造、同時実行という点で、実際に高速化を図ることができます。VB で書き直したことによるパフォーマンスの向上はさまざまな箇所で実現しています。データ構造を見直しています。アルゴリズムの表現を明確にし、リファクタリングの安全性を高めています。非同期処理とスレッドプールを活用しています。Visual Studio のプロファイリング ツールを使って CPU と メモリ割り当てのホットスポットを検出しています。アナライザーを使って不要なボックス化などの単純な .NET パフォーマンス問題を検出しています。例を挙げればきりがありません。

8. ウォッチ ウィンドウでのラムダ式と LINQ 式: 「すばらしい!」 - Visual Studio Uservoice のユーザー Marco Senn-Haag

LINQ 式やラムダ式は、データを要約する優れた手段です。これらが最も問題になるのは、デバッグ時にウォッチ ウィンドウやイミディエイト ウィンドウで式を表示する場合です。以前はこれらのウィンドウで LINQ 式や ラムダ式を使うと、以下のエラーが表示されました (「ラムダ式の評価は、デバッガ内では有効ではありません」)。

Evaluation of lambda expressions is not valid in the debugger.

これが問題なく動作するようになります (図 3 参照)。たとえば、"customers" というコレクションを含むブレークポイントに達したとすると、ウォッチ ウィンドウに次のように書き込めば、このコレクションを簡単に読み取ることができます。

From c In customers Where c.OrderStatus = "Unfulfilled" Select c.LastName

ウォッチ ウィンドウ内のラムダ式と LINQ 式
図 3 ウォッチ ウィンドウ内のラムダ式と LINQ 式

イミディエイト ウィンドウを使えば、プログラムを起動しなくても式を確認できます。たとえば、GetName 関数を含むモジュールの作成を終えていれば、イミディエイト ウィンドウを開き ([Debug] (デバッグ)、[Window] (ウィンドウ)、[Immedeiate] (イミディエイト) の順にクリック)、「? GetName()」と入力すると、その関数の評価が行われます。

Visual Studio 2015 では、LINQ クエリとラムダ式の内部や周辺などのより一般的な状況だけでなく、非同期メソッドや反復子メソッドなどでのエディット コンティニュのサポートを強化して、既存のメソッドへの新規のクエリやラムダ式の追加も可能にする予定です。Visual Studio 2015 Preview ではまだ行えませんが、最終リリースではすべて可能にする予定です。

9. エラー一覧の強化: 「強化前と後の違いがすべて物語っています」 - Visual Basic チームのメンバー Anthony D. Green

Visual Basic 14 のエラー一覧には実践的な強化が数多く行われており、どれも長い間のユーザーの要望に答えるものです (図 4 参照)。強化前のエラー一覧では、完全修飾の型名を示していましたが、強化後は型修飾を最小限に抑えて表示するため、エラー メッセージが読みやすくなります。またエラー コードを表示するため、エラー コード別の並べ替えも簡単になります。さらに、エラー コードにインターネット検索へのハイパーリンクを設定することで、多くの場合に、MSDN マニュアル ページへのリンクよりも使いやすくなっています。エラー一覧の各列は、Excel のようにフィルタリングできます。

Visual Studio 2013 (上) よりも読みやすく、用途が広がった Visual Studio 2015 のエラー一覧 (下)
図 4 Visual Studio 2013 (上) よりも読みやすく、用途が広がった Visual Studio 2015 のエラー一覧 (下)

大きな変更を加える場合、たまに、多くのダウンストリーム コードが機能にしなくなる状態に陥ることがよくあります。以前の VB は先頭から 101 件しかエラーを表示しませんでした。そのためどこまで問題が広がっているか把握しづらかったり、行うべき変更の種類についての概要がわかりづらくなっていました。Visual Basic 14 では、すべてのエラーが制限なく表示されるようになります (これは [Tools] (ツール)、[Options] (オプション)、[Text Editor] (テキスト エディター)、[Basic] (Basic)、[Advanced] (Advanced) の順に開き、[Show diagnostics for closed files] (閉じているファイルの診断結果を表示する) チェック ボックスで構成します)。

言語自体の強化

10. Null 値反映演算子: 「Null 値のチェックが絶えず必要になるため、生産性に影響し、バグを生みやすくなります」 - .NET MVP Deborah Kurata

Customer クラスの Address フィールドは Null 値になってもかまわないとします。このクラスを使用するアプリは、住所の入力を必須とはしていません。これまでは、住所を表示するなど、住所を使ってなんらかの処理を行うコードは、住所が Null 値になることを考慮に入れて、安全策として Null 値のチェックを行う必要がありました。このような Null チェックは非常に面倒です。Visual Basic 14 では、新しい "?." 演算子を使って、このような Null 値になる可能性を巧みに処理することができます。

Console.WriteLine("{0} ({1})",
  customer.Name,
  customer.Address?.Country)

上記の "?." 演算子は、一時変数に代入してから Null 値のチェックを行う以下のような複雑な共通パターンの省略表現にすぎません。

Dim _temp = customer.Address
Console.WriteLine("{0} ({1})",
  customer.Name,
  If(_temp Is Nothing, Nothing, _temp.Country))

"?." をシーケンス内で使用し標準のドット演算子に混在させる (a?.b.c?.d など) こともできます。このシーケンスは左から右に評価されます。"?." によって Null 値と評価されるとこのシーケンスの評価を停止し、Nothing を返します。"." で Null 値と評価された場合は通常どおり NullReferenceException をスローします。

"?." 演算子は、"." 演算子の Null 条件バージョンです。同様に、他のほとんどの演算子に Null 条件バージョンがあります。インデックス処理には array?(i)、デリゲート呼び出しには delegate?(args)、辞書検索には dict?!key、XML 軸プロパティには xml?.@attr、xml?.<key>、xml?...<key> があります。

他にも以下のように "?." を使う手軽な方法もあります。

If customer?.Age > 50 Then ...
' If branch taken only if customer is non-null AND is older than 50
Dim name = If(customer?.Name, "blank")
' Pick a default name if customer is null
Dim first = customers?.FirstOrDefault()
' Only invoke this method if customers is non-null

11. 複数行の文字列リテラル: 「複数行の文字列リテラルには興奮することは間違いありません」 - フォーラム ユーザー Scaramouche

これまでは複数行にわたる文字列を表現する場合は以下のように vbCrLf を多用していました。

Dim json = "{" & vbCrLf &
"  'Name': 'Bad Boys'," & vbCrLf &
"  'ReleaseDate': '1995-4-7T00:00:00'," & vbCrLf &
"  'Genres': ['Action','Comedy']" & vbCrLf &
"}"

もっともなことですが、複数行にわたる文字列リテラルを使いたいという要望が数多く寄せられていました。Visual Basic 14 では以下のように記述できるようになります。

Dim json = "{
  'Name': 'Bad Boys',
  'ReleaseDate': '1995-4-7T00:00:00',
  'Genres': ['Action','Comedy']
}"

これに関連する注目すべき機能として、同様に要望の多かった複数行のステートメントにコメントを挿入できるようになります。これまでは、以下のように LINQ 式の内部にコメントを挿入することはできませんでした。

Dim q = From x In y ' This is a from clause
        Where x < z ' And this is the where
        Select x    ' This select is redundant

12. 文字列補間: 「文字列補間によりコードがはるかにシンプルになり、意図がわかりやすくなります」 - Channel9 ユーザー Judah

文字列補間は、以下のように式を含む文字列を簡単に記述できるようにする手法です。

Dim s = $"hello {p.Name} you are {p.Height:0.00}m tall"

これは、以下の構文を簡単に表現したものです。

Dim s = String.Format("hello {0} you are {1:0.00}m tall", p.Name, p.Height)

文字列補間により、位置を指定するプレースホルダー {0} や {1} の辻褄を合わせる必要がなくなるため、多くの場合、String.Format への明示的な呼び出しよりも容易になります。当然、完全な色付けが行われ、式の穴埋めには IntelliSence が機能します。文字列補間は、以下の例のように、プログラム文字列にも非常にうまく機能します。

Dim fn = $"C:\Documents\{folder}\{file}.{ext}"
Dim url = $"http://{site}/{path}/{file}?search={query}"

String.Format で通常行われるのと同様に、文字列補間では現在のカルチャを使って文字列の書式が設定されます。浮動小数点数を含むプログラム文字列を作成していて、Web サービスに緯度と経度を渡す場合、ほぼ間違いなく InvariantCulture が必要になるでしょう。これは Visual Studio 2015 でのサポートを予定していますが、本稿執筆時点ではまだ仕様が固まっていません。

文字列補間は Visual Studio 2015 Preview にはまだ含めていませんが、最終版前には Visual Studio 2015 に含める予定です。

13. NameOf: 「この機能は数多くのシナリオに役立つでしょう」 - Roslyn フォーラム メンバー ewwloyd

NameOf 演算子は、文字列リテラルがソース コード内の名前を指しているときに、コードにその文字列リテラルを埋め込む優れた方法です。以下に例を示します。

Sub f(s As String)
  If s Is Nothing Then Throw New ArgumentNullException(NameOf(s))
End Sub

NameOf 演算子は実行時には評価されません。これはコンパイル時定数です。上記の場合は定数文字列 "s" です。NameOf(s) を使うのは入力ミスを防ぐのが目的です。たとえば、メソッド パラメータの名前を変更すると、NameOf 引数の名前が自動的に変更されます。このような自動変更は文字列リテラルの場合には行われません。以下は、NameOf が役立つもう 1 つの例です。

Private _age As Integer
Property Age As Integer
  Get
    Return _age
  End Get
  Set
    _age = Value
    RaiseEvent PropertyChanged(
      Me, New PropertyChangedEventArgs(NameOf(Age)))
  End Set
End Property

NameOf は Visual Studio 2015 Preview にはまだ含めていませんが、最終版前には Visual Studio 2015 に含める予定です。

14. オープン ソース: 「コミュニティを作ろうと試みています。世界には優れた開発者がたくさんいます。コミュニティから要望を自身のアイデアとまったく同様に扱います」 - C#/Visual Basic アーキテクト Anders Hejlsberg

最後の強化点は Visual Basic 自体に関するものではなく、VB に取り組むプロセスに関するものです。

VB コンパイラのソース コードはオープン ソースになります。言語自体の設計プロセスも同じです。それぞれの新機能の企画はオープンにされ、完全に公開され精査されます。Microsoft Visual Basic の言語設計チームのメンバーは、基本的には言語の世話役です。チームは企画に目を通し、深く考察し、予期せぬ不具合や問題がないかどうか確認し、言語に含める水準に達しているかどうか判断します。言語設計会議の議事録は広く公開されています。Visual Basic 言語設計チームの一員であることは本当に楽しく、VB のユーザーの 1 人としてすばらしい時間をすごしています。

まとめ

Visual Basic 14 では多くの機能強化が行われています。今回は、ほぼその半分を紹介しました。チーム全体のテーマは、難しく新しい概念を導入しないで、既存の VB を使い勝手の良いまま強化することです。詳細については roslyn.codeplex.com (英語) および blogs.msdn.com/vbteam (英語) を参照してください。

本稿は、Visual Basic 14 と Visual Studio 2015 のプレリリース版を前提としています。記載している情報は変更される可能性があります。


Lucian Wischik はマイクロソフトのVisual Basic/C# 言語設計チームに所属し、特に VB を担当しています。マイクロソフトに入社する以前は、大学で同時実行の理論と非同期について学んでいました。熱心なセーリング愛好家で、長距離の水泳選手でもあります。連絡先は lwischik@microsoft.com (英語のみ) です。

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Dustin Campbell および Anthony D. Green に心より感謝いたします。
Dustin Campbell は Visual Studio チームの主任プログラム マネージャーを努め、Visual Basic および C# IDE のエクスペリエンスに取り組んでいます。彼はこの 5 年間、Project Roslyn の一環として、VB、C# コンパイラおよび IDE のリビルドに専念していました。

Anthony D. Green はマイクロソフトで VB/C#/F# チームのプログラム マネージャーを務め、VB および C# コンパイラの書き直しを (VB と C# で) 行っていました。彼は 14 才から BASIC プログラミング言語、主に QBasic と VB でプログラミングを続け、2004 年からは VB.NET コミュニティの熱心なメンバーとなっています。