Visual Basic 言語は 1991 年に誕生して以来、アプリケーションを構築するための非常に効率的なツールとして機能してきました。 20 年近く経った今でも、Microsoft .NET Framework との簡単な統合を提供し続けており、開発者はデスクトップ、電話、ブラウザ、さらにはクラウドにまたがるアプリケーションを作成できます。
Microsoft は今月、Visual Basic バージョン 10 (VB 2010 または VB10 と呼ばれることもあります) を含む Visual Studio 2010 をリリースします。このリリースはこれまでで最も強力であり、開発者がより少ないコード行でより多くの成果を達成できるように、多くの時間を節約する機能が含まれています。ここでは、Visual Studio 2010 の Visual Basic を完全に理解し、活用するために必要なすべてのコンテンツが提供されます。
共進化
以前は、Visual Basic と C# は別々のチームによって開発されていたため、機能が最初に一方の言語で登場し、次にもう一方の言語で登場することがよくありました。たとえば、C# には、Visual Basic にはない自動実装されたプロパティとコレクション初期化子があり、Visual Basic には、C# にはない遅延バインディングやオプションのパラメーターなどの機能があります。しかし、ある言語に新しい機能が追加されるたびに、多くの顧客はその機能を他の言語にも追加することを求めます。
このニーズに対処するために、Microsoft は Visual Basic チームと C# チームを統合し、共進化戦略を実装しました。目的は、これらの言語の共通開発を促進することです。主要な機能が 1 つの言語で導入されると、それは他の言語でも同様に表示されます。すべての機能が両方の言語に存在し、まったく同じように機能するというわけではありません。実際、各言語には独自の歴史、魂、雰囲気があり、これらの特徴を保持することが重要です。
.NET Framework 4 では、Visual Basic と C# がこの目標に向けて大きな一歩を踏み出し、それぞれが互いの既存の機能の多くを吸収しました。ただし、共進化は以前の機能に影響を与えるだけでなく、これらの言語の将来の開発に対する戦略でもあります。この精神に基づき、.NET Framework 4 では、動的言語ランタイム、埋め込み相互運用型、汎用バリアンスなどの強力な新機能が両方の言語に導入されており、Visual Basic および C# 開発者が .NET Framework を最大限に活用できるようになります。
Visual Basic 2010 の新機能
Visual Basic 2010 の新機能は、より少ないコード行でより多くのことを実行できるように設計されています。私たちの Visual Basic 設計チームは、開発者が通常、多くの退屈な定型コードを記述する必要がある箇所を詳しく調べ、代わりにコンパイラーに作業を行わせる方法を見つけました。もちろん、これは全体的な見方ですので、各機能を詳しく見てみましょう。
暗黙的な行継続文字
Visual Basic は、読みやすさを高めるために英語に似た明確な構文を使用する行指向の言語です。しかし、これによりコードが 1 行あたり 80 文字の制限に達することが多く、開発者は大量のスクロールを余儀なくされます。アンダースコア文字を使用すると、次の行の処理を現在の行として継続する必要があることをコンパイラに指示できます (つまり、複数の物理行を 1 つの論理行として扱うことができます)。しかし、アンダースコア文字を繰り返し入力する必要があるのは常に煩わしいことであり、実際、何年もの間、機能に対する要望の上位にあったのは、コンパイラにこの問題を修正してもらうことでした。
Visual Basic 2010 では、コンパイラーがこの問題を解決できます。コンパイラーは、どのトークン (カンマ、括弧、演算子など) が行継続文字の前に出現する傾向があるかを認識し、文字を挿入するため、開発者は文字を挿入する必要がなくなりました。たとえば、Visual Basic ステートメントをコンマで終了するのは論理的ではありません。コンパイラはこれを認識しているため、{comma, enter} などのトークンのストリームを認識すると、行継続文字の存在を推測します。図1の例を示します。
図 1 行継続文字の推測
<Extension()>
関数 FilterBy Country(
ByVal 顧客 IEnumerable(Of Customer) として、
ByVal 国を文字列として) IEnumerable (顧客の) として
ディムクエリ=
顧客からの
ここで、c.国 = 国
<顧客> を選択します
<%=
c.名前と
、&
c.国
%>
</顧客>
クエリを返す
終了機能
Visual Basic 2008 では、図 1 のコードには 9 個のアンダースコア文字が必要です。ただし、次の各ケースでは、コンパイラーはアンダースコア文字が必要なタイミングを推測し、アンダースコア文字の無視を許可します。
<Extension()> 属性の後
メソッド宣言の ((左括弧) の後
最初のパラメータの , (カンマ) の後
メソッド宣言の ) (右括弧) の前
= (等号) の後
<%= の後 (埋め込み式の開始タグ)
XML テキスト内の各アンパサンド (アンパサンド) の後に
%> の前 (埋め込み式の終了タグ)
この新しいコンパイラ機能は、メソッド シグネチャに特に役立ちます。この例では、80 文字を超える文字でも正常に動作します (各部分が同じ行にある場合)。図 2 には、行継続文字が暗黙的に含まれるタグと位置のすべての組み合わせが表示されます。
図2 行継続文字が暗黙的である場合
マーク | 前に | 後 |
, (カンマ)、. (ピリオド)、> (属性)、( { (左括弧)、<%= (埋め込み式開始タグ (XML テキスト)) | × | |
)、}、] (右括弧)、%> (埋め込み式終了タグ) | × | |
すべての LINQ キーワード: 集計、個別、From、グループ化、グループ結合、結合、Let、並べ替え、選択、スキップ、スキップしながら、テイク、テイク ホワイト、Where、In、Into、On、昇順、降順 | × | × |
オペレーター: +、-、*、/、/、^、>>、<<、Mod、&、+=、-=、*=、/=、/=、^=、>>=、<<=、& =、<、<=、>、>=、<>、Is、IsNot、Like、And、Or、Xor、AndAlso、OrElse | × | |
あり (オブジェクト初期化子内) | × |
ご覧のとおり、言語にはアンダースコア文字が不要な場所が 60 以上あります。 (実際、この記事のコード例では行継続文字が必要ありません。) もちろん、アンダースコア文字も引き続き使用できるため、以前のバージョンの Visual Basic のコードも期待どおりにコンパイルされます。
ステートメントラムダ
ラムダという用語は最初は怖く聞こえるかもしれませんが、ラムダは別の関数内で定義された単なる関数です。 Visual Basic 2008 では、Function キーワードを使用したラムダ式が導入されました。
Dim customers As Customer() = ...
Array.FindAll(customers, Function(c) c. Country = Canada)
ラムダ式を使用すると、ロジックを複数のメソッドに分割することなく、きめ細かくコンパクトな方法でローカルにロジックを表現できます。たとえば、Visual Basic 2005 での以前のコードの表現は次のとおりです (ラムダ式はサポートされていません)。
Dim query = Array.FindAll(customers, AddressOf Filter)
...
関数フィルター (顧客としての ByVal c) ブール値として
c.国 = カナダを返す
終了機能
残念ながら、Visual Basic 2008 のラムダ式では値を返す式が必要なため、次のコードになります。
Array.ForEach(customers, Function(c) Console.WriteLine(c.Country))
次のような状況が発生します。
'Compile error: Expression does not produce a value.
Console.WriteLine は Sub プロシージャ (C# では void) であるため、値を返さないため、コンパイラはエラーを生成します。この状況に対処するために、Visual Basic 2010 ではステートメント ラムダのサポートが導入されました。ステートメント ラムダは、1 つ以上のステートメントを含むラムダです。
Array.ForEach(customers, Sub(c) Console.WriteLine(c.Country))
Console.WriteLine は値を返さないため、Function ラムダの代わりに Sub ラムダを作成するだけです。複数のステートメントを使用した別の例を次に示します。
Array.ForEach(customers, Sub(c)
Console.WriteLine(国名:)
Console.WriteLine(c. Country)
エンドサブ)
このコードを実行すると、顧客ごとに 2 行が出力されます。また、コーディング中に c の上にマウスを置くと、コンパイラーが型を Customer として推論することがわかります (型を明示的に宣言するために c As Customer と入力することも正当です)。イベント ハンドラーを動的に記述することも、ステートメント ラムダの優れた使い方です。
AddHandler b.Click, Sub(sender As Object, e As EventArgs)
MsgBox(ボタンがクリックされました)
'ここにさらに複雑なロジックを挿入します
エンドサブ
実際、Visual Basic 2008 で導入された機能である緩やかな委任でステートメント ラムダを使用できます。 (デリゲート (型安全な関数ポインター) を使用して、複数の関数を一度に実行できます。) この組み合わせにより、シグネチャが非常に単純になります。
AddHandler b.Click, Sub()
MsgBox(ボタンがクリックされました)
'ここにさらに複雑なロジックを挿入します
エンドサブ
デリゲートの緩さにより、イベント ハンドラー内のパラメーターを完全に無視できます。これは、パラメーターがまったく使用されない限り、つまり視覚的に邪魔になるだけである限り、大きな利点です。
これまで見てきた単一行の Sub ラムダと複数行の Sub ラムダに加えて、Visual Basic 2010 は複数行の Function ラムダもサポートしています。
Dim query = customers.Where(Function(c)
'保存されていない顧客のみを返す
'ここにさらに複雑なロジックを挿入します
c.ID = -1 を返す
終了機能)
ステートメント ラムダのもう 1 つの興味深い側面は、Visual Basic 2008 で導入された匿名デリゲートとどのように交差するかです。これらのデリゲートは、厳密には同じではありませんが、C# の匿名メソッドとよく混同されます。匿名委任は、Visual Basic コンパイラがラムダのメソッド シグネチャに基づいてデリゲート型を推論するときに発生します。
Dim method = Function(product As String)
製品 = 紙の場合
4.5 ' ユニットの在庫を返す
それ以外
他のすべての 10 '10 を返します
終了の場合
終了機能
MsgBox(メソッド(紙))
このコードを実行すると、メッセージ ボックスに値 4.5 が表示されます。さらに、メソッドの上にマウスを置くと、Dim メソッド As <Function(String) As Double> というテキストが表示されます。実際のデリゲート型を提供しなかったため、コンパイラは次のように自動的にデリゲート型を生成します。
Delegate Function $compilerGeneratedName$(product As String) As Double
これは、書かれたコードではなく、コンパイラによって生成されたコードにのみ現れるため、匿名デリゲートと呼ばれます。実際に、ラムダの戻り値の型を指定する As 句が指定されていない場合、コンパイラは戻り値の型を Double と推論することに注意してください。コンパイラはラムダ内のすべての return ステートメントを調べ、Double (4.5) と Integer (10) の型を決定します。
'Notice the As Single
Dim メソッド = Function(product As String) As Single
製品 = 紙の場合
4.5 ' ユニットの在庫を返す
それ以外
他のすべての 10 '10 を返します
終了の場合
終了機能
次に、ベースライン型アルゴリズムを実行し、10 を Double に安全に変換できるが、4.5 を Integer に安全に変換できないと判断します。したがって、Double がより適切な選択となります。
戻り値の型を明示的に制御することもできます。その場合、コンパイラは型を推測しません。コンパイラに依存してデリゲート型を推論するのではなく、明示的なデリゲート型を持つ変数にラムダを代入することが非常に一般的です。
Dim method As Func(Of String, Single) =
機能(製品)
製品 = 紙の場合
4.5 ' ユニットの在庫を返す
それ以外
他のすべての 10 '10 を返します
終了の場合
終了機能
明示的なターゲット型が提供されるため、As String または As Single を宣言する必要はなく、コンパイラはステートメントの左側のデリゲート型に基づいてその存在を推測できます。したがって、product の上にマウスを移動すると、推論された型が String であることがわかります。デリゲート型がすでにその情報を提供しているため、「単一として」を指定する必要はなくなりました。前の例では、Func デリゲート (.NET Framework に含まれている) の署名は次のようになります。
Delegate Function Func(Of T, R)(ByVal param As T) As R
一般的な差異に関するセクションで後述するように、小さな例外が 1 つあります。
自動実装されたプロパティ
Visual Basic では、プロパティはオブジェクトの状態を外部に公開するクラス メンバーです。一般的なプロパティ宣言は次のようになります。
Private _Country As String
文字列としてのプロパティ国
得る
戻る_国
終了取得
Set(ByVal 値を文字列として)
_国 = 値
エンドセット
終了プロパティ
実際には 10 行のコードで構成される非常にシンプルなコンセプトです。一般的なオブジェクトには多数のプロパティがあることが多いため、クラス定義に多くの定型コードを含めることになります。このようなタスクを簡略化するために、Visual Basic 2010 では自動実装プロパティが導入されており、これを使用すると、コードを 1 行だけで簡単なプロパティを定義できます。
Property Country As String
この場合、コンパイラは実行を継続し、ゲッター、セッター、サポート フィールドを自動的に生成します。サポートされているフィールドの名前は、常にアンダースコア文字が前に付いた属性の名前になります (この例では _country)。この命名規則により、自動実装プロパティを通常のプロパティに変更する際のバイナリ シリアル化互換性が保証されます。バイナリ シリアル化は、バッキング フィールドの名前が同じである限り引き続き機能します。
自動的に実装されたプロパティを使用して実行できる便利な機能の 1 つは、コンストラクターの実行時にプロパティのデフォルト値を設定する初期化子を指定することです。たとえば、エンティティ クラスの一般的なシナリオでは、主キーを -1 のような値に設定して、未保存の状態であることを示します。コードは次のようになります。
Property ID As Integer = -1
コンストラクターが実行されると、バッキング フィールド (_ID) が値 -1 に自動的に設定されます。イニシャライザ構文は参照型にも機能します。
Property OrderList As List(Of Order) = New List(Of Order)
型の名前を 2 回入力する必要がないため、前のコード行にはそれほど明らかな Visual Basic の特徴がない可能性があります。幸いなことに、Visual Basic で許可されている構文と一致する、通常の変数宣言の短い構文があります。
Property OrderList As New List(Of Order)
この構文をオブジェクト初期化子で使用して、他のプロパティを設定できるようにすることもできます。
Property OrderList As New List(Of Order) With {.Capacity = 100}
明らかに、より複雑なプロパティの場合は、拡張構文が依然として必要です。 Property{Tab} と入力して古いプロパティ フラグメントをアクティブにすることもできます。あるいは、プロパティの最初の行を入力した後、Get{Enter} と入力するだけで、IDE によって古いスタイルのプロパティが生成されます。
Property Name As String
得る
終了取得
Set(ByVal 値を文字列として)
エンドセット
終了プロパティ
新しいプロパティ構文はパブリック フィールドの構文とほぼ同じなので、代わりにパブリック フィールドを使用しないのはなぜでしょうか?理由はいくつかあります。
.NET データ バインディング インフラストラクチャのほとんどは、フィールドではなくプロパティの観点から機能します。
インターフェイスはフィールドの存在を強制することはできませんが、属性の存在を強制することはできます。
プロパティは、ビジネス ルールの変更に対する長期的な柔軟性を提供します。たとえば、誰かが電話番号の長さは 10 桁でなければならないというルールを導入したとします。パブリックフィールドに割り当てられている場合、この検証は実行できません。パブリック フィールドをプロパティに変更することは、バイナリのシリアル化やリフレクションなどのシナリオにとって重大な変更です。
コレクション初期化子
.NET の一般的な方法では、コレクションをインスタンス化し、要素ごとに Add メソッドを 1 回呼び出してコレクションにデータを設定します。
Dim digits As New List(Of Integer)
数字.加算(0)
数字.加算(1)
数字.加算(2)
数字.加算(3)
数字.加算(4)
数字.加算(5)
数字.加算(6)
数字.加算(7)
数字.加算(8)
数字.加算(9)
しかし、基本的に単純な概念であるため、構文上のオーバーヘッドが多くなります。 Visual Basic 2010 では、コレクションのインスタンス化を容易にするためにコレクション初期化子が導入されました。このコードの場合:
Dim digits = New List(Of Integer) From {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
コンパイラは、Add メソッドへのすべての呼び出しを自動的に生成します。 Visual Basic の新規としての構文機能を使用することもできます。
Dim digits As New List(Of Integer) From {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
Visual Basic チームでは、オプション推論設定の変更に対するコードの回復力が高くなるため、前者ではなく 2 番目の構文 (新規として) を使用することを常に推奨していることに注意してください。
コレクション初期化子は、次の要件を満たす任意の型に対して使用できます。
For Each ステートメントを使用して型を反復処理できます。つまり、型は IEnumerable を実装します。 (コレクション型のより正確で詳細な定義については、msdn.microsoft.com/library/aa711986(VS.71).aspx にある Visual Basic 言語仕様のセクション 10.9.3 を参照してください)。
この型には、アクセス可能な (ただしパブリックである必要はない) パラメーターなしのコンストラクターがあります。
この型には、アクセス可能な (ただしパブリックである必要はない) インスタンスまたは Add という名前の拡張メソッドがあります。
これは、辞書などのより複雑な型に対してコレクション初期化子も使用できることを意味します。
Dim lookupTable As New Dictionary(Of Integer, String) From
{{1、1}、
{2、2}、
{3、3}、
{4、4}}
(このステートメントは 5 行にわたっていますが、アンダースコア文字がないことに注意してください。) この場合、コンパイラーは、辞書を初期化する古い方法と同等のコードを生成します。
Dim lookupTable As New Dictionary(Of Integer, String)
lookupTable.Add(1, One)
lookupTable.Add(2, Two)
lookupTable.Add(3, Three)
lookupTable.Add(4, Four)
コンパイラは、1 つではなく 2 つのパラメータを指定して Add メソッドを呼び出しています。コレクション初期化子に渡される値が {{1, One}, {2, Two}, …} のように入れ子になった中括弧内にあるため、これを行うことができます。入れ子になった中括弧のセットごとに、コンパイラーはこれらのパラメーターを互換性のある Add メソッドに渡そうとします。
拡張メソッドを使用して、独自のカスタム Add 実装を提供することもできます。
<Extension()>
Sub Add(ByVal ソースとして IList(Of Customer),
ByVal id を整数として、
ByVal 名を文字列として、
ByVal city As String)
source.Add(新規顧客の場合
{
.ID = ID、
.Name = 名前、
.City = 都市
})
エンドサブ
(欠落しているアンダースコア文字をすべて見てください!) このメソッドは、IList(Of Customer) を実装する型を拡張し、次のように新しいコレクション初期化子構文を使用できるようにします。
Dim list = New List(Of Customer) From
{
{1、ジョン、レドモンド}、
{2、ボブ、シアトル}、
{3、サリー、トロント}
}
(リストに 3 人の顧客を追加します)。自動的に実装されたプロパティを持つコレクション初期化子を使用することもできます。
Property States As New List(Of String) From {AL, AK, AR, AZ, ...}
配列リテラル
Visual Basic 2010 では、コレクション型を操作するためのより強力な方法に加えて、配列を操作するための強力な機能強化も提供しています。次のコードを想定します (古いバージョンでは正常に動作します)。
Dim numbers As Integer() = New Integer() {1, 2, 3, 4, 5}
この配列内の要素を見ると、各要素が整数であることは明らかなので、この行で実際に整数を 2 回出力する必要があることは、実際には値を追加するものではありません。配列リテラルを使用すると、配列のすべての要素を中括弧内に配置し、コンパイラーに型を自動的に推測させることで配列を作成できます。
Dim numbers = {1, 2, 3, 4, 5}
数値の型は Object ではなく Integer() です (Option Infer が有効である限り)。その理由は、配列リテラルがそれ自体を表し、独自の型を持つようになったためです。より複雑な例を想定してみましょう。
Dim numbers = {1, 2, 3, 4, 5.555}
この場合、数値の型は Double() として推論されます。コンパイラは、ステートメント ラムダの戻り値の型を推論するために前述したのと同じアルゴリズムを使用して、配列内の各要素を調べて基本型を計算することによって型を決定します。基本型がない場合はどうなりますか?たとえば、次のコードに示すように:
Dim numbers = {1, 2, 3, 4, 5}
この場合、Integer を String に変換すると変換範囲が減少します (つまり、実行時にデータ損失が発生する可能性があります)。同様に、String を Integer に変換すると変換範囲も減少します。選択できる唯一の安全な型は Object() です (Option Strict が有効な場合、コンパイラはエラーを生成します)。
配列リテラルをネストして、多次元配列またはギザギザ配列を形成できます。
'2-dimensional array
ディム行列 = {{1, 0}, {0, 1}}
'ギザギザ配列 - 括弧により、内側の配列が最初に評価されます。
薄暗いギザギザ = { ({1, 0}), ({0, 1}) }
動的言語ランタイム
Visual Basic は技術的には本質的に静的言語ですが、遅延バインディングなどの非常に強力な動的機能を常に備えています。 Visual Studio 2010 には、動的言語ランタイム (DLR) と呼ばれる新しいプラットフォームが付属しており、これを使用すると、動的言語の構築と動的言語間の通信が容易になります。 Visual Basic 2010 は、後期バインダーで DLR を完全にサポートするように更新され、開発者が他の言語 (IronPython/IronRuby など) で開発されたライブラリやフレームワークを使用できるようになりました。
この機能の際立った利点は、構文的に何も変更されていないことです (実際、この機能をサポートするためにコンパイラ内のコードは 1 行も変更されていません)。開発者は、以前のバージョンの Visual Basic と同様に遅延バインディング操作を実行できます。変更されたのは Visual Basic ランタイム (Microsoft.VisualBasic.dll) のコードで、DLR によって提供される IDynamicMetaObjectProvider インターフェイスが認識されるようになりました。オブジェクトがこのインターフェイスを実装している場合、Visual Basic ランタイムは DLR CallSite を構築し、オブジェクトとそれを提供する言語が独自のセマンティクスを操作に挿入できるようにします。
たとえば、Python 標準ライブラリには、random.py というファイルが含まれています。このファイルには、配列内の要素をランダムに再配置するために使用できる shuffle と呼ばれるメソッドがあります。このメソッドの呼び出しは簡単です。
Dim python As ScriptRuntime = Python.CreateRuntime()
Dim ランダム As Object = python.UseFile(random.py)
ディム項目 = {1、2、3、4、5、6、7}
ランダム.シャッフル(アイテム)
実行時に、Visual Basic はオブジェクトが IDynamicMetaObjectProvider を実装していることを認識し、制御を DLR に渡します。DLR は Python と通信してメソッドを実行します (Visual Basic で定義された配列をパラメーターとしてメソッドに渡します)。
これは DLR 対応 API を呼び出す例ですが、開発者はこの機能を使用する独自の API を作成することもできます。重要なのは、IDynamicMetaObjectProvider インターフェイスを実装することです。この場合、Visual Basic および C# コンパイラは、特別な動的セマンティクスを持つオブジェクトを認識します。このインターフェイスを手動で実装しないでください。System.Dynamic.DynamicObject クラス (このインターフェイスは既に実装されています) から継承し、いくつかのメソッドのみをオーバーライドする方が簡単です。図 3 は、カスタム動的オブジェクト (オンザフライでプロパティを作成するように見えるプロパティ バッグ) を作成し、通常の Visual Basic 遅延バインディングを使用してオブジェクトを呼び出す完全な例を示しています。 (DynamicObject の使用の詳細については、blogs.msdn.com/vbteam/archive/2010/01/20/fun-with-dynamic-objects-doug-rothaus.aspx にある Doug Rothaus の優れた記事を参照してください。)
図 3 カスタム動的オブジェクトを作成し、Visual Basic 遅延バインディングを使用してオブジェクトを呼び出す
Imports System.Dynamic
モジュール モジュール1
サブメイン()
Dimp As Object = 新しい PropertyBag
p.One = 1
p.2 = 2
p.3 = 3
Console.WriteLine(1 ページ目)
Console.WriteLine(p.2)
Console.WriteLine(p.3)
エンドサブ
クラス PropertyBag : DynamicObject を継承します
プライベート値を新しい辞書として(文字列、整数)
パブリックオーバーライド関数 TrySetMember(
ByVal バインダー SetMemberBinder として、
ByVal 値 (オブジェクトとして) ブール値として
値(バインダー.名前) = 値
True を返す
終了機能
パブリックオーバーライド関数 TryGetMember(
ByVal バインダー GetMemberBinder として、
ByRef の結果 (オブジェクトとして) ブール値として
戻り値.TryGetValue(binder.Name, result)
終了機能
終了クラス
エンドモジュール
一般的な分散
これは確かに最初は複雑に聞こえるかもしれない関数です (共分散や反分散などの用語を使うと) が、実際には非常に単純です。 IEnumerable(Of Apple) 型のオブジェクトがあり、それを IEnumerable(Of Fruit) に割り当てたい場合、すべての Apple は Fruit であるため (継承によって強制される)、これは正当であるはずです。残念ながら、Visual Basic 2010 より前は、実際には共通言語ランタイム (CLR) でジェネリック バリアンスがサポートされていたとしても、コンパイラではジェネリック バリアンスがサポートされていませんでした。
図 4 の例を見てみましょう。 Visual Basic 2008 では、図 4 のコードは Dim EnabledOnly 行でコンパイル エラーを生成します (または、Option Strict が無効になっている場合は、ランタイム例外が生成されます)。回避策は、次のように .Cast 拡張メソッドを呼び出すことです。
'Old way, the call to Cast(Of Control) is no longer necessary in VB 2010
Dim EnabledOnly = FilterEnabledOnly(buttons.Cast(Of Control))
Visual Basic 2010 では、Out 修飾子を使用して IEnumerable インターフェイスが共変としてマークされているため、これは必要なくなりました。
Interface IEnumerable(Of Out T)
...
エンドインターフェース
図 4 一般的な分散の例
Option Strict On
パブリッククラスフォーム1
Sub Form1_Load() は MyBase.Load を処理します
ボタンを新しいリスト (ボタン) として暗くする
{
新しいボタン付き
{
.Name = btnOk、
.Enabled = True
}、
新しいボタン付き
{
.Name = btnキャンセル、
.Enabled = False
}
}
Dim EnabledOnly = FilterEnabledOnly(ボタン)
エンドサブ
関数 FilterEnabledOnly(
ByVal コントロールとして IEnumerable(Of Control)
) IEnumerable(制御の)として
C In コントロールから戻る
ここで、c.Enabled = True
終了機能
終了クラス
これは、ジェネリック パラメーター T が変数になり (つまり、継承に適している)、コンパイラーは、型がインターフェイスから取得される場合にのみパラメーターが使用されることを保証します。ジェネリック パラメーターは逆変数にすることもできます。つまり、入力された場所でのみ使用されます。実際には型は両方を持つことができます。たとえば、前に説明した Func デリゲートには、反変パラメーター (渡されるもの) と共変パラメーター (戻り値の型) の両方があります。
Delegate Function Func(Of In T, Out R)(ByVal param As T) As R
カスタム インターフェイスとデリゲートで In および Out 修飾子を使用できます。 .NET Framework 4 の多くの一般的なインターフェイスとデリゲートは変数としてマークされています。一般的な例には、すべての Action/Func デリゲート、IEnumerable(Of T)、IComparer(Of T)、IQueryable(Of T) などが含まれます。
ジェネリックバリアンスの優れた点は、それがまったく心配する必要のない機能であることです。機能が機能している場合は、決して気付かないでしょう。かつてはコンパイラ エラーが発生した状況、または .Cast(Of T) の呼び出しが必要だった状況は、Visual Basic 2010 では正常に動作します。
オプションパラメータの改善
オプションのパラメーターは、開発者がより柔軟なメソッドを構築し、多くのメソッドのオーバーロードでクラスが乱雑になることを回避できる便利な効率化機能を提供します。以前は、オプションのパラメーターを null (または非内部構造型) にすることはできないというわずかな制限がありました。 Visual Basic 2010 では、任意の値型のオプションのパラメーターを定義できるようになりました。
Sub DisplayOrder(ByVal customer As Customer,
ByVal orderID を整数として、
オプションの ByVal 単位 整数 = 0 として?
オプションの ByVal 背景色を色として = なし)
エンドサブ
この例では、ユニットのタイプは Nullable(Of Integer) であり、backgroundColor は非コンテンツ構造タイプですが、これらは依然としてオプションのパラメータとして使用されます。 Visual Basic 2010 では、汎用のオプション パラメーターのサポートも強化されています。
組み込み相互運用タイプ
COM 相互運用を実行するアプリケーションの一般的な弱点は、プライマリ相互運用アセンブリ (PIA) を使用する必要があることです。 PIA は、COM コンポーネント上でランタイム呼び出し可能ラッパー (RCW) として機能する .NET アセンブリであり、それを識別する一意の GUID を持っています。 .NET アセンブリは PIA と通信し、COM と .NET の間でデータを移動するために必要なマーシャリングを実行します。
残念ながら、PIA はエンド ユーザー コンピュータに展開する必要がある追加の DLL であるため、展開が複雑になる可能性があります。また、バージョン管理の問題が発生する可能性もあります。たとえば、アプリケーションを Excel 2003 と Excel 2007 の両方で動作させたい場合は、アプリケーションに両方の PIA を展開する必要があります。
組み込み相互運用タイプの機能はアプリケーションに直接埋め込まれますが、絶対に必要な PIA のタイプとメンバーのみが埋め込まれるため、エンド ユーザーのコンピューターに PIA を展開する必要はありません。
既存のオブジェクトに対してこの機能を有効にするには (新しい参照に対してデフォルトで有効になります)、ソリューション エクスプローラーで参照を選択し、[プロパティ] ウィンドウで [相互運用タイプの埋め込み] オプションを変更します (図 5 を参照)。あるいは、ming コマンド ライン コンパイラでコンパイルする場合は、/r および /reference の代わりに /l (または /link) スイッチを使用します。
図 5 ソリューション エクスプローラーでの埋め込み相互運用タイプの有効化
この機能を有効にすると、アプリケーションは PIA に依存しなくなります。実際、Reflector または ildasm でアセンブリを開くと、実際には PIA への参照がまったくないことがわかります。
複数の目標
Visual Basic 2010 のすべての機能の最も優れた点は、.NET Framework 2.0 から .NET Framework 3.5 を対象とするプロジェクトでもそれらの機能を使用できることです。つまり、暗黙的な行継続文字、配列リテラル、コレクション初期化子、ステートメント ラムダ、自動的に実装されるプロパティなどの機能は、.NET Framework 4 を再ターゲットしなくても、既存のプロジェクトですべて利用できるようになります。
例外は、.NET Framework 4 でのみ使用可能な型に依存する埋め込み相互運用型です。そのため、.NET Framework バージョン 2.0 ~ 3.5 を対象とする場合は、この機能を使用できません。さらに、変数としてマークされた型は、.NET Framework 4 での状態でのみマークされるため、前の例では、バージョン 2.0 から 3.5 を対象とする場合でも、.Cast(Of T) を呼び出す必要があります。ただし、これらの以前のバージョンを対象とする場合は、(In/Out 修飾子を使用して) 独自の変数タイプを作成できます。
アプリケーションの現在のターゲット フレームワークを変更するには、[マイ プロジェクト] をダブルクリックし、[コンパイル] タブをクリックし、[詳細コンパイル オプション] をクリックして、下部のコンボ ボックスから選択します。
実際には、ming コマンド ラインからコンパイルするときにこの機能を有効にする ming コマンド ライン スイッチはありません。実際、コンパイラは、どのアセンブリが System.Object (通常は mscorlib) の定義を提供しているか、およびアセンブリの対象となっているフレームワークを調べて、出力アセンブリにその値をマークします。 (コンパイラは、Silverlight アセンブリを生成するときにこれと同じメカニズムを使用します。) IDE を使用する場合、これらすべてが透過的に行われるため、通常は何も心配する必要はありません。
ようこそお試しください
ご覧のとおり、Visual Basic 2010 には、作成するコード行を減らしてコンパイラにより多くの作業を実行させながら、生産性を向上させる強力な機能が多数備わっています。この記事では言語機能についてのみ説明しましたが、Visual Basic 2010 IDE には数え切れないほどの優れた機能強化があります。いくつかの機能強化を以下に示します。
に移動します
引用を強調表示する
使用から生まれる
IntelliSense の向上 (部分文字列マッチング、キャメルケース検索、提案パターン – 最初に開発スタイルをテストするのに役立ちます)
マルチモニターのサポート
ズーム
Visual Basic チームは、Visual Basic を改善するための取り組みについて皆様からのフィードバックをお待ちしております。Microsoft Connect でコメントやご質問をお送りください。言語と IDE 機能の詳細については、msdn.com/vbasic で記事、例、ハウツー ビデオなどのコンテンツを確認してください。もちろん、学習するための最良の方法は製品を詳しく調べて使用することなので、ここでインストールして試してみましょう。