ラムダ式 (Visual Basic)

ラムダ式は、デリゲートが有効であれば使用できる、名前を持たない関数またはサブルーチンです。ラムダ式は、単一行または複数行の関数またはサブルーチンとして作成できます。ラムダ式には、現在のスコープから値を渡すことができます。

[!メモ]

RemoveHandler ステートメントは例外です。ラムダ式は、RemoveHandler のデリゲート パラメーターには渡せません。

ラムダ式は、標準の関数またはサブルーチンを作成する場合と同様に、Function キーワードまたは Sub キーワードを使用して作成できます。ただし、ラムダ式はステートメント内に含まれます。

次の例は、引数をインクリメントし、その値を返すラムダ式を示します。この例では、ラムダ式関数の単一行と複数行の両方の構文を示しています。

Dim increment1 = Function(x) x + 1
Dim increment2 = Function(x)
                     Return x + 2
                 End Function

' Write the value 2.
Console.WriteLine(increment1(1))

' Write the value 4.
Console.WriteLine(increment2(2))

次の例は、コンソールに値を書き込むラムダ式です。この例では、ラムダ式サブルーチンの単一行と複数行の両方の構文を示しています。

Dim writeline1 = Sub(x) Console.WriteLine(x)
Dim writeline2 = Sub(x)
                     Console.WriteLine(x)
                 End Sub

' Write "Hello".
writeline1("Hello")

' Write "World"
writeline2("World")

上記の例では、ラムダ式が変数名に割り当てられていることに注意してください。この場合、変数を参照するたびにラムダ式を呼び出すことになります。次の例に示すように、ラムダ式の宣言と呼び出しを同時に行うこともできます。

Console.WriteLine((Function(num As Integer) num + 1)(5))

ラムダ式は、関数呼び出しの戻り値として返すことができます (このトピックの「コンテキスト」セクションに例が示されています)。また、次の例に示すように、デリゲート型を受け取るパラメーターの引数として渡すこともできます。

Module Module2

    Sub Main()
        ' The following line will print Success, because 4 is even.
        testResult(4, Function(num) num Mod 2 = 0)
        ' The following line will print Failure, because 5 is not > 10.
        testResult(5, Function(num) num > 10)
    End Sub

    ' Sub testResult takes two arguments, an integer value and a 
    ' delegate function that takes an integer as input and returns
    ' a boolean. 
    ' If the function returns True for the integer argument, Success
    ' is displayed.
    ' If the function returns False for the integer argument, Failure
    ' is displayed.
    Sub testResult(ByVal value As Integer, ByVal fun As Func(Of Integer, Boolean))
        If fun(value) Then
            Console.WriteLine("Success")
        Else
            Console.WriteLine("Failure")
        End If
    End Sub

End Module

ラムダ式の構文

ラムダ式の構文は、標準の関数やサブルーチンの構文に似ています。相違点を次に示します。

  • ラムダ式には名前がありません。

  • ラムダ式では、Overloads や Overrides などの修飾子を使用できません。

  • 単一行のラムダ関数では、As 句を使用して戻り値の型を指定することはできません。代わりに、ラムダ式の本体を評価した値から型が推論されます。たとえば、ラムダ式の本体が cust.City = "London" の場合、その戻り値の型は Boolean になります。

  • 複数行のラムダ関数では、As 句を使用して戻り値の型を指定するか、As 句を省略して戻り値の型を推論することができます。複数行のラムダ関数で As 句を省略した場合、戻り値の型は、複数行のラムダ関数のすべての Return ステートメントの中で最も優先度の高い型であると推論されます。主要な型その他のすべてのタイプに広げることができますが一意の型します。この一意の型を特定できない場合、最も優先度の高い型は、配列内の他のすべての型に縮小変換できる一意の型になります。これらの一意の型をどちらも特定できない場合は、Object が最も優先度の高い型になります。この場合、Option Strict が On に設定されていると、コンパイラ エラーになります。

    式を指定する場合など、 Returnステートメントには、値型にはが含まれてInteger、 Long、およびDouble、結果の配列ですDouble。Integer と Long はどちらも Double に拡大変換され、Double だけになります。そのため、Double が最も優先度の高い型になります。詳細については、「拡大変換と縮小変換 (Visual Basic)」を参照してください。

  • 単一行の関数の本体は、ステートメントではなく、値を返す式である必要があります。単一行の関数用の Return ステートメントはありません。単一行の関数によって返される値は、関数の本体に指定された式の値です。

  • 単一行のサブルーチンの本体は、単一行のステートメントである必要があります。

  • 単一行の関数およびサブルーチンには、End Function ステートメントや End Sub ステートメントは含まれません。

  • ラムダ式のパラメーターのデータ型は As キーワードを使用して指定するか、推論することができます。すべてのパラメーターは、データ型が指定されているか推論される必要があります。

  • Optional パラメーターと Paramarray パラメーターは使用できません。

  • ジェネリック パラメーターは使用できません。

非同期のラムダ

ラムダ式とステートメントを使用して非同期処理を組み込むことを簡単に作成することができます、 Async (Visual Basic)Await 演算子 (Visual Basic)キーワードします。たとえば、次の Windows フォームの例を呼び出すし、非同期メソッドを待機するイベント ハンドラーが含まれているExampleMethodAsync。

Public Class Form1
 
    Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        ' ExampleMethodAsync returns a Task.
        Await ExampleMethodAsync()
        TextBox1.Text = vbCrLf & "Control returned to button1_Click."
    End Sub
 
    Async Function ExampleMethodAsync() As Task
        ' The following line simulates a task-returning asynchronous process.
        Await Task.Delay(1000)
    End Function
 
End Class

非同期ラムダを使用して、同じイベント ハンドラーを追加することができます、 AddHandler ステートメント。このハンドラーを追加するには、追加、 Async修飾子はラムダ パラメーターのリストを次のようにします。

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        AddHandler Button1.Click, 
            Async Sub(sender1, e1)
                ' ExampleMethodAsync returns a Task.
                Await ExampleMethodAsync()
                TextBox1.Text = vbCrLf & "Control returned to Button1_ Click."
            End Sub
    End Sub

    Async Function ExampleMethodAsync() As Task
        ' The following line simulates a task-returning asynchronous process.
        Await Task.Delay(1000)
    End Function

End Class

作成し、非同期のメソッドを使用する方法の詳細についてを参照してくださいAsync および Await を使用した非同期プログラミング (C# および Visual Basic)

コンテキスト

ラムダ式は、それが定義されている外側のスコープとコンテキストを共有します。ラムダ式は、コンテナー スコープに記述されているすべてのコードと同じアクセス権を持ちます。これには、コンテナー スコープ内のメンバー変数、関数、サブ関数、Me、パラメーター、およびローカル変数へのアクセスも含まれます。

コンテナー スコープ内のローカル変数とパラメーターは、そのスコープの有効期間以降もアクセスされる可能性があります。ラムダ式を参照するデリゲートがガベージ コレクションの対象にならない限り、元の環境での変数へのアクセスは保持されます。次の例では、変数 target は、ラムダ式 playTheGame が定義されているメソッド makeTheGame にローカルです。返されたラムダ式は、Main で takeAGuess に割り当てられ、ローカル変数 target へのアクセスを保持し続けていることに注意してください。

Module Module6

    Sub Main()
        ' Variable takeAGuess is a Boolean function. It stores the target
        ' number that is set in makeTheGame.
        Dim takeAGuess As gameDelegate = makeTheGame()

        ' Set up the loop to play the game.
        Dim guess As Integer
        Dim gameOver = False
        While Not gameOver
            guess = CInt(InputBox("Enter a number between 1 and 10 (0 to quit)", "Guessing Game", "0"))
            ' A guess of 0 means you want to give up.
            If guess = 0 Then
                gameOver = True
            Else
                ' Tests your guess and announces whether you are correct. Method takeAGuess
                ' is called multiple times with different guesses. The target value is not 
                ' accessible from Main and is not passed in.
                gameOver = takeAGuess(guess)
                Console.WriteLine("Guess of " & guess & " is " & gameOver)
            End If
        End While

    End Sub

    Delegate Function gameDelegate(ByVal aGuess As Integer) As Boolean

    Public Function makeTheGame() As gameDelegate

        ' Generate the target number, between 1 and 10. Notice that 
        ' target is a local variable. After you return from makeTheGame,
        ' it is not directly accessible.
        Randomize()
        Dim target As Integer = CInt(Int(10 * Rnd() + 1))

        ' Print the answer if you want to be sure the game is not cheating
        ' by changing the target at each guess.
        Console.WriteLine("(Peeking at the answer) The target is " & target)

        ' The game is returned as a lambda expression. The lambda expression
        ' carries with it the environment in which it was created. This 
        ' environment includes the target number. Note that only the current
        ' guess is a parameter to the returned lambda expression, not the target. 

        ' Does the guess equal the target?
        Dim playTheGame = Function(guess As Integer) guess = target

        Return playTheGame

    End Function

End Module

次の例では、入れ子になったラムダ式の広範囲のアクセス権を示します。返されたラムダ式は、Main から aDel として実行されるときに、これらの要素にアクセスします。

  • ラムダ式が定義されるクラスのフィールド: aField

  • ラムダ式が定義されるクラスのプロパティ: aProp

  • ラムダ式が定義される functionWithNestedLambda メソッドのパラメーター: level1

  • functionWithNestedLambda のローカル変数: localVar

  • ラムダ式がネストされるラムダ式のパラメーター: level2

Module Module3

    Sub Main()
        ' Create an instance of the class, with 1 as the value of 
        ' the property.
        Dim lambdaScopeDemoInstance = 
            New LambdaScopeDemoClass With {.Prop = 1}

        ' Variable aDel will be bound to the nested lambda expression  
        ' returned by the call to functionWithNestedLambda.
        ' The value 2 is sent in for parameter level1.
        Dim aDel As aDelegate = 
            lambdaScopeDemoInstance.functionWithNestedLambda(2)

        ' Now the returned lambda expression is called, with 4 as the 
        ' value of parameter level3.
        Console.WriteLine("First value returned by aDel:   " & aDel(4))

        ' Change a few values to verify that the lambda expression has 
        ' access to the variables, not just their original values.
        lambdaScopeDemoInstance.aField = 20
        lambdaScopeDemoInstance.Prop = 30
        Console.WriteLine("Second value returned by aDel: " & aDel(40))
    End Sub

    Delegate Function aDelegate(
        ByVal delParameter As Integer) As Integer

    Public Class LambdaScopeDemoClass
        Public aField As Integer = 6
        Dim aProp As Integer

        Property Prop() As Integer
            Get
                Return aProp
            End Get
            Set(ByVal value As Integer)
                aProp = value
            End Set
        End Property

        Public Function functionWithNestedLambda(
            ByVal level1 As Integer) As aDelegate

            Dim localVar As Integer = 5

            ' When the nested lambda expression is executed the first 
            ' time, as aDel from Main, the variables have these values:
            ' level1 = 2
            ' level2 = 3, after aLambda is called in the Return statement
            ' level3 = 4, after aDel is called in Main
            ' locarVar = 5
            ' aField = 6
            ' aProp = 1
            ' The second time it is executed, two values have changed:
            ' aField = 20
            ' aProp = 30
            ' level3 = 40
            Dim aLambda = Function(level2 As Integer) _
                              Function(level3 As Integer) _
                                  level1 + level2 + level3 + localVar +
                                    aField + aProp

            ' The function returns the nested lambda, with 3 as the 
            ' value of parameter level2.
            Return aLambda(3)
        End Function

    End Class
End Module

デリゲート型への変換

ラムダ式は、互換性のあるデリゲート式に明示的に変換できます。互換性の一般的な要件については、「厳密でないデリゲート変換 (Visual Basic)」を参照してください。たとえば、次のコード例は、明示的に Func(Of Integer, Boolean) または対応するデリゲート シグネチャに変換するラムダ式を示しています。

' Explicitly specify a delegate type.
Delegate Function MultipleOfTen(ByVal num As Integer) As Boolean

' This function matches the delegate type.
Function IsMultipleOfTen(ByVal num As Integer) As Boolean
    Return num Mod 10 = 0
End Function

' This method takes an input parameter of the delegate type. 
' The checkDelegate parameter could also be of 
' type Func(Of Integer, Boolean).
Sub CheckForMultipleOfTen(ByVal values As Integer(),
                          ByRef checkDelegate As MultipleOfTen)
    For Each value In values
        If checkDelegate(value) Then
            Console.WriteLine(value & " is a multiple of ten.")
        Else
            Console.WriteLine(value & " is not a multiple of ten.")
        End If
    Next
End Sub

' This method shows both an explicitly defined delegate and a
' lambda expression passed to the same input parameter.
Sub CheckValues()
    Dim values = {5, 10, 11, 20, 40, 30, 100, 3}
    CheckForMultipleOfTen(values, AddressOf IsMultipleOfTen)
    CheckForMultipleOfTen(values, Function(num) num Mod 10 = 0)
End Sub

次のコード例は、暗黙的に Sub(Of Double, String, Double) または対応するデリゲート シグネチャに変換するラムダ式を示しています。

Module Module1
    Delegate Sub StoreCalculation(ByVal value As Double,
                                  ByVal calcType As String,
                                  ByVal result As Double)

    Sub Main()
        ' Create a DataTable to store the data.
        Dim valuesTable = New DataTable("Calculations")
        valuesTable.Columns.Add("Value", GetType(Double))
        valuesTable.Columns.Add("Calculation", GetType(String))
        valuesTable.Columns.Add("Result", GetType(Double))

        ' Define a lambda subroutine to write to the DataTable.
        Dim writeToValuesTable = Sub(value As Double, calcType As String, result As Double)
                                     Dim row = valuesTable.NewRow()
                                     row(0) = value
                                     row(1) = calcType
                                     row(2) = result
                                     valuesTable.Rows.Add(row)
                                 End Sub

        ' Define the source values.
        Dim s = {1, 2, 3, 4, 5, 6, 7, 8, 9}

        ' Perform the calculations.
        Array.ForEach(s, Sub(c) CalculateSquare(c, writeToValuesTable))
        Array.ForEach(s, Sub(c) CalculateSquareRoot(c, writeToValuesTable))

        ' Display the data.
        Console.WriteLine("Value" & vbTab & "Calculation" & vbTab & "Result")
        For Each row As DataRow In valuesTable.Rows
            Console.WriteLine(row(0).ToString() & vbTab &
                              row(1).ToString() & vbTab &
                              row(2).ToString())
        Next

    End Sub


    Sub CalculateSquare(ByVal number As Double, ByVal writeTo As StoreCalculation)
        writeTo(number, "Square     ", number ^ 2)
    End Sub

    Sub CalculateSquareRoot(ByVal number As Double, ByVal writeTo As StoreCalculation)
        writeTo(number, "Square Root", Math.Sqrt(number))
    End Sub
End Module

ラムダ式をデリゲートに割り当てるときやプロシージャに引数として渡すときは、パラメーター名を指定してデータ型を省略することで、デリゲートから型が取得されるようにできます。

  • 次の例は、null 許容引数に値が割り当てられた場合は True を返し、値が Nothing の場合は False を返すラムダ式を定義します。

    Dim notNothing =
      Function(num? As Integer) num IsNot Nothing
    Dim arg As Integer = 14
    Console.WriteLine("Does the argument have an assigned value?")
    Console.WriteLine(notNothing(arg))
    
  • 次の例は、配列内の最後の要素のインデックスを返すラムダ式を定義します。

    Dim numbers() = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    Dim lastIndex =
      Function(intArray() As Integer) intArray.Length - 1
    For i = 0 To lastIndex(numbers)
        numbers(i) += 1
    Next
    

参照

処理手順

方法 : Visual Basic でプロシージャを別のプロシージャに渡す

方法: ラムダ式を作成する (Visual Basic)

関連項目

Function ステートメント (Visual Basic)

Sub ステートメント (Visual Basic)

概念

Visual Basic におけるプロシージャ

Visual Basic における LINQ の概要

null 許容値型 (Visual Basic)

厳密でないデリゲート変換 (Visual Basic)

その他の技術情報

デリゲート (Visual Basic)