MSDN マガジン > Home > 発行物 > 2007 > September >  基本的な本能: ラムダ式
基本的な本能
ラムダ式
Timothy Ng

Visual Basic® 2008 で導入されたラムダ式は、どのプログラマにとっても役立つ新たなツールです。ラムダ式は、関数内で定義される呼び出し可能なエンティティであり、何の制約もなく利用できます。したがって、ラムダ式を関数から返したり他の関数に渡したりできます。ラムダ式が Visual Basic 2008 (以前のコード名は "Orcas") に追加されたのは、LINQ (Language Integrated Queries) をサポートするためです。これは、Visual Basic でのデータ プログラミングを可能にするものです (後で詳しく説明します)。ラムダ式を使用するにつれ、能力と柔軟性が向上することがわかるでしょう。これからラムダ式の基本的な概念のサンプルを示し、その利点と、それを使用して表現力に富むプログラムを記述する方法を説明していきます。

ラムダ式とは
基本的なラムダ式の定義の例を次に示します。ここでは、doubleIt を、整数を受け取って整数を返すラムダ式として定義します。ラムダ式は、入力を効率的に受け取り、それを 2 倍した結果を返します。
Dim doubleIt As Func(Of Integer, Integer) = _
    Function(x As Integer) x * 2
Func 型も Visual Basic 2008 で新しく導入されました。これは、実質的にはデリゲートで、最後のジェネリック パラメータとして指定された戻り値の型を持ち、その前にジェネリック パラメータとして最大 4 つの引数を指定できます (実際には複数の Func デリゲートがあり、それぞれが特定の数のパラメータを受け取ります)。Func デリゲート型は、System 名前空間のアセンブリ System.Core.dll で定義されます。Visual Basic で作成されたすべての新規プロジェクトは、自動的に System.Core.dll への参照を保持するので、すぐに Func 型を利用できます。
次のコードは Func のさまざまなオーバーロードを示しています。
Dim f0 As Func(Of Boolean)
Dim f1 As Func(Of Integer, Boolean)
Dim f4 As Func(Of Integer, Integer, Integer, Integer, Boolean)
f0 はブール型を返すデリゲート、f1 は整数を受け取ってブール型を返すデリゲート、f4 は 4 つの整数引数を受け取ってブール型を返すデリゲートです。注意すべき重要なポイントは、ラムダ式がデリゲートとして型指定される点、つまり Visual Basic 2005 のデリゲートと同じように呼び出し可能なエンティティである点です。
最初のコードの代入式の右側は、新しいラムダ式の構文です。これは、キーワード Function で始まり、その後に 1 つの引数リストと 1 つの式が続きます。
前の例では、ラムダ式は、1 つの引数 x (整数) を受け取っていました。しかし、お気付きのように return ステートメントはありません。これは、Visual Basic コンパイラが、式に基づいて既に型を認識しているからです。このため、return ステートメントは不要です。この場合、x が整数なので、2 * x も整数になり、ラムダ式の結果も整数になります。
ラムダ式の不思議な力は、次に示すように通常のデリゲートと同様に単純にそれを呼び出すことができることです。
Dim doubleIt As Func(Of Integer, Integer) = _
    Function(x As Integer) x * 2
Dim z = doubleIt(20)
このコードを実行すると、z に格納される値は 40 になります。ここで作成したのは、基本的に、入力した任意の整数値を 2 倍にするラムダ式です。
次に、もう少し複雑な例、ラムダ式ファクトリについて考えてみましょう。
Dim mult As Func(Of Integer, Func(Of Integer, Integer)) = _
    Function(x As Integer) Function(y As Integer) x * y
Mult は、かなり複雑なラムダ式です。これは、1 つの整数引数を入力として受け取り、別のラムダ式を返します。返される式は 1 つの整数引数を入力として受け取って整数を返します。構文の記述には少し注意が必要ですが、次のような一連の行と書式を使用すると、正確なネスト構造を示すのに役立ちます。
Dim mult As Func(Of Integer, Func(Of Integer, Integer)) = _
    Function(x As Integer) _
        Function(y As Integer) x * y
もう少しわかりやすくいうと、外側のラムダ式に、別のラムダ式が含まれており、これをコンパイラが return ステートメントとして使用します。内側のラムダ式のシグネチャは、外側のラムダ式の戻り値の Func(Of Integer, Integer) デリゲートのシグネチャと一致するので、コンパイラは、ステートメントをエラーなしでコンパイルします。
このようなラムダ式はどのように使用するのでしょうか。
Dim mult_10 = mult(10)
Dim r = mult_10(4)
この 1 行目では、mult_10 を mult(10) と定義しています。Mult(10) は、引数を受け取ってそれを 10 倍するラムダを返すので、mult_10 の型は Func(Of Integer, Integer) です。2 行目では、値 4 を渡して mult_10 を呼び出しているので、結果 r は 40、r の型は整数になります。
基本的に、mult はラムダ式ファクトリです。これは、1 つ目の引数によってカスタマイズされるラムダ式を返します。お気付きかもしれませんが、内側のラムダ式は、外側のラムダ式のパラメータを参照しますが、内側のラムダ式の有効期間は外側の式の有効期間より長くなります。この変数の変換については後で説明します。

コールバックとしてのラムダ式
ラムダ式は単なるデリゲートなので、デリゲートを使用できるあらゆる場所で使用できます。次に、デリゲートを受け取り、リスト内の要素ごとにデリゲートを呼び出すメソッドについて考えてみましょう。
Delegate Function ShouldProcess(Of T)(element As T) As Boolean

Sub ProcessList(Of T)( _
        elements As List(Of T), shouldProcess As ShouldProcess(Of T))
    For Each elem in elements
        If shouldProcess(elem) Then
            ' Do some processing here
        End If
    Next
End Sub
これは、デリゲートを使用するアプリケーションとしてかなり標準的なものです。メソッド ProcessList は、リストの各要素をループし、その項目の処理が必要かどうかをチェックし、標準的な処理を行います。
これを Visual Basic 2005 で使用するには、次のように (赤字のコードに注意してください) デリゲートと同じシグネチャを持つクラスまたはモジュールで関数を定義し、その関数のアドレスを ProcessList プロシージャに渡す必要がありました。
Class Person
    Public age As Integer
End Class

Function _PrivateShouldProcess(person As Person) As Boolean
    Return person.age > 50
End Function

Sub DoIt()
    Dim list As New List(Of Person)
    ' Obtain list of Person from a database, for example
    ProcessList(list, AddressOf _PrivateShouldProcess)
End Sub
これは、ひいき目に見ても面倒な作業です。多くの場合、コード ドキュメントを掘り下げてデリゲートが押し付けるシグネチャを調べ、それと正確に一致させる必要があるからです。また、ProcessList を多数の異なる shouldProcess 関数で呼び出す必要がある場合、多くの小さいプライベート関数を記述することになるのでコードが煩雑になります。
次の関数をラムダ式でどのように呼び出すことができるかを見ていきましょう。
Class Person
    Public age As Integer
End Class

Sub DoIt()
    Dim list As New List(Of Person)
    ' Obtain list of Person from a database, for example
    ProcessList(list, Function(person As Person) person.age > 50)
End Sub    
私はラムダ式の簡潔で洗練されたところを大変気に入っています。処理ロジックの実行に独自の関数を作成する必要はありません。デリゲートは、使用される場所で定義されます。プライベート メソッドで定義されているデリゲートを別の場所にあるメソッドで使用するとそのデリゲートの局所性が失われますが、それに比べてこれははるかに良い方法です。
ラムダ式がいかに強力かつ便利であり、またコードを読んだり管理したりするのも簡単であることがおわかりいただけたと思います。型の推定など、より高度な機能を使用すると、その力はより発揮されます。
注意すべき制限の 1 つは、ラムダ式が厳密に 1 つの式であることです。Visual Basic 2008 では、ラムダ式に 1 つの式しか指定できません。このコラムではさらに、Visual Basic 2008 で導入された新しい 3 項演算子について説明します。これを使用すると、条件式を構築できますが、現在の機能ではラムダ式での任意のステートメントの指定はサポートされません。
ここで、ラムダ式の基になる概念を詳しく説明する前に、そもそもなぜラムダ式が導入されたかを説明します。

ラムダ式が追加された理由
LINQ クエリをサポートするために、いくつかの機能を追加する必要がありましたが、その中に Visual Basic とラムダ式が含まれていました。次のような Visual Basic のクエリ ステートメントがあるとします。
Dim q = From p In Process.GetProcesses() _
        Where p.PriorityClass = ProcessPriorityClass.High _
        Select p
このクエリ ステートメントをコンパイルできるようにするために、内部で多くの処理が行われます。大まかにいうと、コンパイラは、Process.GetProcesses コレクションを反復処理し、それに Where フィルタを適用し、Where 句のフィルタと一致したプロセスのリストを返します。
Where 句内に、Visual Basic の式 PriorityClass = ProcessPriorityClass.High があることに注意してください。このフィルタを実行するために、コンパイラは Where フィルタのラムダ式を作成し、それをプロセス リスト内の各要素に適用します。
Dim q = Process.GetProcesses().Where( _
            Function(p) p.PriorityClass = ProcessPriorityClass.High)
基本的に、ラムダ式は、メソッドの生成とそのデリゲートへの割り当てをすばやく簡単に行う手段をコンパイラに提供します。これはすべて自動的に行われます。デリゲートと関数の組み合わせでは得られないラムダ式の利点は、コンパイラが自動的にラムダ式の型の推定を行うことです。前の例にある引数 p の型は、その使用法によって推定されます。この場合は、Where 引数がラムダ式の型を定義し、引数であるラムダ式の型がコンパイラによって推定されます。コンパイラでの型の推定機能は、Visual Basic に追加された強力な機能です。その働きを見ていきましょう。

型の推定
強力な型の推定メカニズムが導入されたことで、各変数の型がわからなくてもよくなりました。さらに、型の推定によって、これまで不可能だったシナリオが可能になりました。ラムダ式を使用するときに型を推定する 3 種類の方法を見てみましょう。
ラムダ式の引数の型を推定する このシナリオは、ラムダを割り当てるデリゲート型があり、その引数を正確に指定することができない場合に大変便利です。
Dim lambda As Func(Of Integer, Integer) = Function(x) x * x
例では、lambda 変数が Func(Of Integer, Integer) として型指定されています。これは、整数引数を 1 つ受け取って整数引数を返すデリゲートです。結果的に、コンパイラは、lamda 引数 x が整数で、lambda の戻り値が整数になると自動的に推定します。
ラムダ式の引数の型を推定するメリットは、デリゲートを受け取るメソッドを呼び出す場合にも得られます。前の例を変更して考えてみましょう。
Delegate Function ShouldProcess(Of T)(element As T) As Boolean

Sub ProcessList(Of T)( _
        elements As List(Of T), shouldProcess As ShouldProcess(Of T))
    ' Method body removed for brevity
End Sub
この場合、ProcessList 関数は、ラムダ式を受け取ります。
プロシージャを次のように呼び出すことができます。
Sub DoIt()
    Dim list As New List(Of A)
    ' fill or obtain elements in list
    ProcessList(list, Function(a) a.x > 50)
End Sub
ここでは前のようにラムダ引数の型は指定していませんが、コンパイラはそれが Person であると推定します。こうした処理はどのように行われているのでしょうか。この例では、実際には複数のレベルの型の推定が行われます。
まず、コンパイラは、ProcessList が、入力として List(Of T) と ShouldProcess(Of T) を受け取るジェネリック プロシージャであることを確認します。ProcessList の呼び出しで、コンパイラは、リストが最初の引数であることと、そのリストが List(Of Person) であることを確認します。2 つ目の引数には型 T が何かについての手掛かりはないので、コンパイラは、T が Person 型であると判断します。次に、ShouldProcess(Of T) のジェネリック引数が Person であると推定し、そこから 2 番目の引数が ShouldProcess(Of Person) であると推定します。最後に、ラムダ式が自身の引数の型を提供していなかったので、コンパイラは、ShouldProcess(Of Person) のデリゲートのシグネチャに基づいて引数の型を認識し、パラメータ (a) の型を Person と推定します。これは、型の推定のきわめて強力なモデルです。ラムダを構築するときにデリゲート引数の型を認識しておく必要はありません。この難しい仕事はコンパイラに任せることをお勧めします。とはいえ、さいわいにも推定された型が何かを示すのに役立つ IntelliSense® およびツールヒントがあるので、生産性は (それほどではないにしても) 確実に上がります。
結果の型を推定する このシナリオは、デリゲート型がないのでそれをコンパイラに合成させる場合に大変便利です。これは、Visual Basic だけで利用できる機能です。
Dim lambda = Function(x As Integer) x * x
この例では、ラムダ式は、完全に型指定されています (lambda の引数 x は整数型であり、整数 * 整数 = 整数なので、コンパイラは戻り値が整数であると推定します)。しかし、lambda 変数には型がありません。このため、コンパイラは、ラムダ式図形と一致する匿名デリゲートを合成し、そのデリゲート型を lambda に割り当てます。
これは、優れた機能です。なぜなら、ラムダ式を、そのデリゲート型を静的に構築しておかなくてもその場で作成できることを意味するからです。たとえば、複数の変数に適用しなければならない条件があったり、図 1 のようにそれを複数の場所で適用する必要があったりする状況に陥ることは、どれくらいの頻度で起こるでしょうか。私は、コードを記述していてこのような状況に遭遇する回数はかなり多いです。通常は、こうしたことを考慮せずに、条件チェックを関数内にあちこちに配置するのではなく、1 か所で行えるようにします。
Class Motorcycle
    Public color As String
    Public CC As Integer
    Public weight As Integer
End Class

Sub PrintReport(motorcycle As New Motorcycle)
    If motorcycle.color = "Red" And motorcycle.CC = 600 And _
       motorcycle.weight > 300 And motorcycle.weight < 400 Then
       ' do something here
    End If

    ' do something here

    If motorcycle m.color = "Red" And motorcycle.CC = 600 And _
       motorcycle.weight > 300 And motorcycle.weight < 400 Then
       ' do something here
    End If
End Sub

しかし、チェックを使用するのはこの関数だけで、他ではまったく使用されない場合もあります。この関数をサポートするだけのために使用されるランダムなヘルパ関数を記述することで、自分のクラスを煩雑にしたくありません。これは保守性に悪影響を及ぼします。たとえば他の人がこの関数を呼び出す一方で私がそれを変更する必要がある場合はどうなるでしょうか。また、多数のプライベート メソッドを持つクラスでは、名前による追跡が非常に困難になることがあります。さらに、IntelliSense リストのエントリが増える一方なので、IntelliSense の有用度が低くなる可能性があります。さらに、ロジックの局所性にも影響があります。個別のプライベート メソッドを作成する場合は、それを使用するメソッドから物理的に近いところに配置するのが私の好みです。多くの人々が同じコード ベースで作業する場合は、この局所性を長期間維持するのが難しいことがあります。図 2 のように、ラムダ式を使用してコンパイラにデリゲート クラスを自動作成させると、この問題に対処できます。
Sub PrintReport(motorcycle As New Motorcycle)
    Dim check = Function(m As Motorcycle) m.color = "Red" And _
                                          m.CC = 600 And _
                                          m.weight > 300 And _
                                          m.weight < 400
    If check(motorcycle) Then
        ' do something here
    End If

    ' do something here

    If check(motorcycle) Then
        ' do something here
    End If
End Sub

私は、Motorcycle クラスで一部の条件をチェックするロジックを不利益のあるプライベート メソッドに組み込むことは考えないようにしていました。しかし、ラムダ式であれば話は別です。メソッドと同じ感覚でラムダ式を呼び出せるように、コンパイラが自動的に (内部で) デリゲート型を作成し、必要な作業をすべて結び付けるからです。
このアプローチは私の大のお気に入りです。ロジックを実装の近く (メソッド本体内) に配置すると、それが考慮されて (1 つのコピーだけ)、コンパイラが保守の大部分を引き受けるからです。これは、ラムダ式の本体として任意の複雑な式を構築できるので効果的です。
遅延バインディング — オブジェクトを推定する このシナリオでは、lambda 変数もラムダ式も型指定されません。
Dim lambda = Function(x) x * x
ここでは、匿名デリゲートもコンパイラによって生成されますが、lambda の型は System.Object です。これは、このシナリオで遅延バインディングが有効になるのは、Option Strict が off の場合であることを意味します。
これは、遅延バインディングに依存している人にとっては非常に好都合なシナリオです。遅延バインディング処理は、ラムダ式で完全にサポートされているので、上の例では、* 演算子が lambda に指定する型で定義されている限り有効です。
Dim a = lambda(10)
Dim b = lambda(CDec(10))
Dim c = lambda("This will throw an exception because " & _
               "strings don't support the * operator")
この例でわかるように、ランタイム型に * 演算子がある限り、すべて適切に機能します。このラムダ式の特性は、Visual Basic の遅延バインディング モデルに非常にうまく適合します。

内部でのコード生成
これまでラムダ式について説明してきましたが、今度はコンパイラが生成するコードの種類について見てみましょう。前の例について考えます。
Sub TestLambda()
    Dim doubleIt As Func(Of Integer, Integer) = _
        Function(x As Integer) x * 2
    Console.WriteLine(doubleIt(10))
End Sub
Func はデリゲートであり、デリゲートは単に関数へのポインタであることがわかっています。さて、コンパイラはどのようにしてこの不思議な力を活用しているのでしょう。この場合、コンパイラは、新しい関数を出力し、デリゲートを新しい関数をポイントするように設定します。
Function $GeneratedFunction$(x As Integer) As Integer
    Return x * 2
End Sub

Sub TestLambda()
    Dim doubleIt As Func(Of Integer, Integer) = _
        AddressOf $GeneratedFunction$
    Console.WriteLine(doubleIt(10))
End Sub
コンパイラは、基本的にラムダ式を受け取り、その内容で新しい関数を作成し、代入ステートメントを変更して、ラムダ式が生成された関数のアドレスを受け取るようにします。この場合、関数は、ラムダ式を使用するメソッドを含む同じ親の下に生成します。TestLambda がクラス C で定義されている場合、生成された関数は C で定義されます。なお、生成された関数は呼び出し可能ではなく、private としてマークされます。

ラムダ式と変数変換
前の例で、ラムダ式の本体は、ラムダに渡された変数を参照していました。しかし、ラムダ式の力は、変数変換で大きく発揮されます。前に触れたように、コンパイラは特定のシナリオで "不思議な力を発揮" します。このようなシナリオを見ていく前に、ラムダ計算と呼ばれる数学の一分野の基本的な概念について説明します。ラムダ式は同様の概念に基づいています。
ラムダ計算の基本的な概念は、関数が自由変数または束縛変数を持てるということです。自由変数は、それを含むメソッドの変数 (ローカルおよびパラメータ) で定義される変数です。束縛変数は、ラムダのシグネチャで定義される変数、または基本クラスなど、ラムダを含むクラスのメンバです。
ラムダ式では束縛変数と自由変数を区別することが非常に重要です。それらがラムダ式のセマンティクス、生成されるコード、そして最終的にはプログラムの正確さに影響するからです。ここで、束縛変数と自由変数を含むラムダ式の例を示します。
Dim y As Integer = 10
Dim addTen As Func(Of Integer, Integer) = Function(ByVal x) x + y
ここで、x は、ラムダ式の仮パラメータなので、ラムダ内の束縛変数と見なされます。また、y は、ラムダ式でそれを含むメソッドに属する変数なので自由変数と見なされます。
ラムダ式を定義した後はデリゲート型のように扱われることを思い出してください (たとえば、メソッドからラムダ式を返すことができます)。次の例について考えてみましょう。
Function MakeLambda() As Func(Of Integer, Integer)
    Dim y As Integer = 10
    Dim addTen As Func(Of Integer, Integer) = Function(ByVal x) x + y
    Return addTen
End Function

Sub UseLambda()
    Dim addTen = MakeLambda()
    Console.WriteLine(addTen(5))
End Sub
このコードは、UseLambda が呼び出されたときに "15" をコンソールに出力します。しかし、どのように処理されているのかが気になっているかもしれません。関数 MakeLambda は y をローカル変数として定義し、ラムダは y を使用していますが、ラムダは関数 MakeLambda から値を返します。関数 UseLambda は MakeLambda からラムダを受け取り、そのラムダを実行します。変数 y はなぜかラムダで "記憶されている" ように見えます。
これはある種の舞台裏のトリックでしょうか。y は、MakeLambda のメソッド内でのみ有効です。MakeLambda から返されるラムダを実行すると、MakeLambda はスコープ外となるので、そのスタック スペースは削除されます。しかし、y はスタックで定義されていました。そして、何らかの形でラムダと "くっついて" いるようでした。
この粘着性は、一般的に変数変換として呼ばれる魔法のようなものです。この場合、変数 y は変換変数と呼ばれます。また、おわかりのように、変換変数は強力です。コンパイラは、変数の状態をキャプチャし、それを通常の有効期間が過ぎても保持するために、多数の下準備をします。
具体的に説明すると、コンパイラは、自由変数を持つラムダ式を見つけたときに、その自由変数をクロージャと呼ばれるクラスに変換します。クロージャの有効期間は、そこで変換された自由変数の有効期間より長くなります。コンパイラは、クロージャ インスタンス内の変数にアクセスするために、メソッド内の変数アクセスを書き換えます。
まず、MakeLambda の例をもう一度見てみましょう。
Function MakeLambda() As Func(Of Integer, Integer)
    Dim y As Integer = 10
    Dim addTen As Func(Of Integer, Integer) = Function(ByVal x) x + y
    Return Lambda
End Function
前に分析したように、x はラムダのパラメータに束縛されていますが、y は自由変数です。コンパイラは、これを検出して、ラムダ式の定義と自由変数をキャプチャするクロージャ クラスの作成に進みます。
Public Class _Closure$__1
    Public y As Integer
    Public Function _Lambda$__1(ByVal x As Integer) As Integer
        Return x + Me.y
    End Function
End Class
クロージャ変数は、変数 y をキャプチャし、それをクロージャ クラスに保存することがおわかりでしょう。次に自由変数は、クロージャ クラスで束縛変数に変換されます。
コンパイラは、次のようにラムダ式を含むメソッドも書き換えます。
Function MakeLambda() As Func(Of Integer, Integer)
    Dim Closure As New _Closure$__1
    Closure.y = 10
    Return AddressOf Closure._Lambda$__1
End Function
ここで、コンパイラがクロージャ変数を作成し、クロージャ変数に変換されるローカル変数 y を書き換え、変数を初期化し、クロージャ クラスに書き込まれるラムダ式のアドレスを単純に返すことがわかります。
コンパイラはラムダ式の自由変数を変換するだけであることに注意することが重要です。変数の状態はクロージャでキャプチャされます。これは、ラムダ式が存在する限り存在します。
別の例を見てみましょう。
Sub Test()
    Dim y As Integer = 10
    Dim Lambda As Func(Of Integer, Integer) = Function(ByVal x) x + y
    y = 20
    Console.WriteLine(Lambda(5))
End Sub
この関数を実行するとどんな値が表示されますか。答えが 25 であれば、正解です。では、なぜ 25 なのでしょう。コンパイラは、次のようにすべての自由変数 y をキャプチャしてクロージャのコピーに書き換えます。
Sub Test()
    Dim Closure As New $CLOSURE_Compiler_Generated_Name$
    Closure.y = 10
    Dim Lambda = AddressOf Closure.Lambda_1
    Closure.y = 20
    Console.WriteLine(Lambda(5))
End Function
おわかりのように、ラムダ式が実行されるときまでに、y の値が 20 に変更されているので、ラムダ式が実行されたときに 5 + 20 が返されます。
これは、ループに関していえば実に重要です。複数の自由関数が 1 つのクロージャにキャプチャされるので、たとえば、値が変化するキャプチャ済み変数を含むラムダ式を使用するスレッドを起動した場合に予期しない動きをすることがあります。
Sub Test()
    For I = 1 To 5
        StartThread(Function() I + 10)
    Next
End Function
この例では、StartThread が新しいスレッドを作成し、ラムダ式の結果をコンソールに出力するとします。I はクロージャにキャプチャされるので、スレッドがラムダ式を実行するときまでに for ループが変数 I を変更している可能性があります。その場合、プログラムが予想どおりに 11、12、13、14、および 15 を出力しないことがあります。代わりに、キャプチャされた変数のスコープを for ループ内にする必要があります。
Sub Test()
    For I = 1 To 5
        Dim x = I
        StartThread(Function() x + 10)
    Next
End Function
このコードは、今度はクロージャに値 x をキャプチャします。プログラムは予想どおりに 11、12、13、14、および 15 を出力します。どの変数が変換されるか、いつラムダ式が実行されるか、そして変換変数がいつ変更される可能性があるかを知ることはきわめて重要です。それによって、プログラムをあらゆる状況で確実に正しく動作させることができるからです。

ラムダ式を最大限に活用する
Visual Basic 2008 では、ラムダ式の本体として指定できるのは 1 つの式だけですが、完全に型指定された単純な条件式を実行できる新しい 3 項キーワードも導入されています。
Dim x = If(condition, 10, 20)
If キーワードは IIF 関数呼び出しとほぼ同じです。ただし、If キーワードが完全にタイプセーフである点を除きます。前の例の場合、これは、コンパイラは、If キーワードの両方の分岐が整数を返すことを推定するので、型の推定規則を適用して、x の型を整数と判断することを意味します。IIF を使用すると、x は型オブジェクトなります。
If キーワードはラムダ式で使用できます。
Dim x = Function(c As Customer) _
    If(c.Age >= 18, c.Address, c.Parent.Address)
この例において、Customer クラスがあり、お察しのとおりその定義に顧客の現在の住所を示す Address プロパティが含まれているとします。ラムダ式は、3 項式を使用して、「顧客が 18 歳以上である場合は、その住所を返す」という入力引数の条件を適用します。それ以外の場合は親の住所を返します。
ここで、型の推定が始まり、コンパイラは、ラムダ式の戻り値の型が Address であると判断します。続いて、(前に説明したように) x のデリゲート型を作成します。ここでデリゲート型は、入力として Customer を受け取り Address を返します。
ラムダ式の詳細については、私のブログ (blogs.msdn.com/timng (英語)) を購読できます。今後、ラムダ式 (およびその他の Visual Basic 2008 の言語機能) について詳しく説明する予定です。

ご意見やご質問は、

こちら instinct@microsoft.com.

まで英語でお送りください。
Timothy Ng は、マイクロソフトの Visual Basic コンパイラ チームのソフトウェア開発エンジニアで、Visual Studio の次期リリースである Visual Studio 2008 向けに、型の推定、フレンド アセンブリ、式ツリー、匿名型などの機能を開発しました。Tim の連絡先は timng@microsoft.com (英語) です。

Page view tracker