문 - Visual Basic

문은 실행 코드를 나타냅니다.

Statement
    : LabelDeclarationStatement
    | LocalDeclarationStatement
    | WithStatement
    | SyncLockStatement
    | EventStatement
    | AssignmentStatement
    | InvocationStatement
    | ConditionalStatement
    | LoopStatement
    | ErrorHandlingStatement
    | BranchStatement
    | ArrayHandlingStatement
    | UsingStatement
	| AwaitStatement
	| YieldStatement
    ;

메모. Microsoft Visual Basic 컴파일러는 키워드 또는 식별자로 시작하는 문만 허용합니다. 따라서 예를 들어 호출 문 "Call (Console).WriteLine"은 허용되지만 호출 문 "(Console).WriteLine"은 허용되지 않습니다.

제어 흐름

제어 흐름 은 문과 식이 실행되는 시퀀스입니다. 실행 순서는 특정 문 또는 식에 따라 달라집니다.

예를 들어 더하기 연산자(섹션 더하기 연산자)를 평가할 때 먼저 왼쪽 피연산자가 계산되고 오른쪽 피연산자가 계산된 다음 연산자 자체가 계산됩니다. 블록은 먼저 첫 번째 하위 상태를 실행한 다음 블록의 문을 통해 하나씩 진행하여 실행됩니다(섹션 블록 및 레이블).

이 순서에서 암시적으로 실행될 다음 작업인 제어점의 개념입니다. 메서드가 호출될 때(또는 "호출") 메서드의 인스턴스 를 만든다고 합니다. 메서드 인스턴스는 메서드 매개 변수 및 지역 변수의 자체 복사본과 자체 제어점으로 구성됩니다.

일반 메서드

다음은 일반 메서드의 예입니다.

Function Test() As Integer
    Console.WriteLine("hello")
    Return 1
End Function

Dim x = Test()    ' invokes the function, prints "hello", assigns 1 to x

일반 메서드가 호출되면

  1. 먼저 메서드의 인스턴스가 해당 호출과 관련되어 만들어집니다. 이 인스턴스에는 메서드의 모든 매개 변수 및 지역 변수의 복사본이 포함됩니다.
  2. 그런 다음 모든 매개 변수가 제공된 값으로 초기화되고 모든 지역 변수가 해당 형식의 기본값으로 초기화됩니다.
  3. 암시적 지역 변수의 경우 이름이 함수의 이름이고, 형식이 함수의 Function반환 형식이고 초기 값이 해당 형식의 기본값인 함수 반환 변수 라고도 합니다.
  4. 그런 다음 메서드 인스턴스의 제어점은 메서드 본문의 첫 번째 문에서 설정되고 메서드 본문은 여기에서 즉시 실행하기 시작합니다(섹션 블록 및 레이블).

제어 흐름이 메서드 본문을 정상적으로 종료하는 End Function 경우(해당 끝에 도달하거나 End Sub 해당 끝을 표시하는 방법 또는 명시적 Return 또는 Exit 문을 통해) 제어 흐름은 메서드 인스턴스의 호출자에게 반환됩니다. 함수 반환 변수가 있는 경우 호출 결과는 이 변수의 최종 값입니다.

제어 흐름이 처리되지 않은 예외를 통해 메서드 본문을 종료하면 해당 예외가 호출자에게 전파됩니다.

제어 흐름이 종료된 후에는 메서드 인스턴스에 대한 라이브 참조가 더 이상 없습니다. 메서드 인스턴스가 지역 변수 또는 매개 변수의 복사본에 대한 유일한 참조를 보유하는 경우 가비지 수집될 수 있습니다.

반복기 메서드

반복기 메서드는 명령문에서 사용할 For Each 수 있는 시퀀스를 생성하는 편리한 방법으로 사용됩니다. 반복기 메서드는 Yield 문(Section Yield 문)을 사용하여 시퀀스의 요소를 제공합니다. 문이 없는 Yield 반복기 메서드는 빈 시퀀스를 생성합니다. 반복기 메서드의 예는 다음과 같습니다.

Iterator Function Test() As IEnumerable(Of Integer)
    Console.WriteLine("hello")
    Yield 1
    Yield 2
End Function

Dim en = Test()
For Each x In en          ' prints "hello" before the first x
    Console.WriteLine(x)  ' prints "1" and then "2"
Next

반환 형식 IEnumerator(Of T)이 인 반복기 메서드가 호출될 때

  1. 먼저 해당 호출과 관련된 반복기 메서드의 인스턴스가 만들어집니다. 이 인스턴스에는 메서드의 모든 매개 변수 및 지역 변수의 복사본이 포함됩니다.
  2. 그런 다음 모든 매개 변수가 제공된 값으로 초기화되고 모든 지역 변수가 해당 형식의 기본값으로 초기화됩니다.
  3. 암시적 지역 변수를 반복 기 현재 변수라고도 하며, 해당 형식은 해당 형식이고 초기 값은 T 해당 형식의 기본값입니다.
  4. 메서드 인스턴스의 제어점은 메서드 본문의 첫 번째 문에서 설정됩니다.
  5. 그런 다음 이 메서드 인스턴스와 연결된 반복기 개체 가 만들어집니다. 반복기 개체는 선언된 반환 형식을 구현하고 아래에 설명된 대로 동작을 가합니다.
  6. 그러면 호출자에서 제어 흐름이 즉시 다시 시작되고 호출 결과가 반복기 개체입니다. 이 전송은 반복기 메서드 인스턴스를 종료하지 않고 수행되며 최종 처리기가 실행되지 않습니다. 메서드 인스턴스는 반복기 개체에서 계속 참조되며 반복기 개체에 대한 라이브 참조가 있는 한 가비지 수집되지 않습니다.

반복기 개체의 Current 속성에 액세스하면 호출의 현재 변수 가 반환됩니다.

반복기 개체의 MoveNext 메서드가 호출되면 호출에서 새 메서드 인스턴스를 만들지 않습니다. 대신 기존 메서드 인스턴스(및 해당 제어점 및 지역 변수 및 매개 변수)가 사용됩니다. 반복기 메서드가 처음 호출될 때 만들어진 인스턴스입니다. 제어 흐름은 해당 메서드 인스턴스의 제어 지점에서 실행을 다시 시작하고 반복기 메서드의 본문을 정상적으로 진행합니다.

반복기 개체의 Dispose 메서드가 호출되면 기존 메서드 인스턴스가 다시 사용됩니다. 제어 흐름은 해당 메서드 인스턴스의 제어 지점에서 다시 시작되지만 문이 다음 작업인 것처럼 Exit Function 즉시 동작합니다.

반복기 개체의 호출 MoveNext 또는 Dispose 반복기 개체에 대한 동작에 대한 위의 설명은 해당 반복기 개체의 MoveNext 모든 이전 호출이 Dispose 호출자에게 이미 반환된 경우에만 적용됩니다. 그렇지 않은 경우 동작이 정의되지 않습니다.

제어 흐름이 해당 끝에 도달 End Function 하거나 명시적 Return 또는 Exit 문을 통해 반복기 메서드 본문을 정상적으로 종료하는 경우 반복기 개체의 호출 MoveNext 또는 Dispose 함수 컨텍스트에서 반복기 메서드 인스턴스를 다시 시작해야 하며 반복기 메서드가 처음 호출될 때 만들어진 메서드 인스턴스를 사용하게 됩니다. 해당 인스턴스의 제어점은 문에 End Function 남아 있고 제어 흐름은 호출자에서 다시 시작되며 호출에 MoveNext 의해 다시 시작된 경우 값 False 이 호출자에게 반환됩니다.

제어 흐름이 처리되지 않은 예외를 통해 반복기 메서드 본문을 종료하면 예외가 호출자로 전파되며, 이는 다시 호출 MoveNext 또는 Dispose호출이 됩니다.

반복기 함수의 다른 가능한 반환 형식에 관해서는

  • 반환 형식 IEnumerable(Of T) 이 일부 T인 반복기 메서드를 호출하면 메서드의 모든 매개 변수에 대한 반복기 메서드 호출과 관련된 인스턴스가 먼저 만들어지고 제공된 값으로 초기화됩니다. 호출 결과는 반환 형식을 구현하는 개체입니다. 이 개체의 메서드를 호출하면 메서드의 GetEnumerator 모든 매개 변수 및 지역 변수 호출과 관련된 GetEnumerator 인스턴스를 만듭니다. 이미 저장된 값에 대한 매개 변수를 초기화하고 위의 반복기 메서드에 대해 진행합니다.
  • 반환 형식이 제네릭이 아닌 인터페이스 IEnumerator인 반복기 메서드가 호출되면 동작은 정확히 다음과 같습니다 IEnumerator(Of Object).
  • 반환 형식이 제네릭이 아닌 인터페이스 IEnumerable인 반복기 메서드가 호출되면 동작은 정확히 다음과 같습니다 IEnumerable(Of Object).

비동기 메서드

비동기 메서드는 애플리케이션의 UI를 차단하지 않고도 장기 실행 작업을 수행하는 편리한 방법입니다. 비동기에서는 동기 작업이 짧습니다. 즉, 비동기 메서드의 호출자가 즉시 실행을 다시 시작하지만 나중에 비동기 메서드 인스턴스의 최종 완료가 발생할 수 있습니다. 규칙에 따라 비동기 메서드의 이름은 접미사 "Async"로 지정됩니다.

Async Function TestAsync() As Task(Of String)
    Console.WriteLine("hello")
    Await Task.Delay(100)
    Return "world"
End Function

Dim t = TestAsync()         ' prints "hello"
Console.WriteLine(Await t)  ' prints "world"

메모. 비동기 메서드는 백그라운드 스레드에서 실행 되지 않습니다 . 대신 메서드가 연산자를 통해 Await 자신을 일시 중단하고 일부 이벤트에 대한 응답으로 다시 시작되도록 예약할 수 있습니다.

비동기 메서드가 호출되는 경우

  1. 먼저 해당 호출과 관련된 비동기 메서드의 인스턴스가 만들어집니다. 이 인스턴스에는 메서드의 모든 매개 변수 및 지역 변수의 복사본이 포함됩니다.
  2. 그런 다음 모든 매개 변수가 제공된 값으로 초기화되고 모든 지역 변수가 해당 형식의 기본값으로 초기화됩니다.
  3. 일부의 반환 형식 Task(Of T) 을 사용하는 비동기 메서드의 경우 암시적 지역 변수를 작업 반환 변수라고도 합니다. 이 변수의 형식은 T 기본값이며 초기 값은 기본값T입니다.T
  4. 비동기 메서드가 Function 반환 형식 Task 또는 Task(Of T) 일부 T개체인 경우 현재 호출과 연결된 해당 형식의 개체가 암시적으로 생성됩니다. 이를 비동기 개체 라고 하며 비동기 메서드의 인스턴스를 실행하여 수행할 향후 작업을 나타냅니다. 이 비동기 메서드 인스턴스의 호출자에서 컨트롤이 다시 시작되면 호출자는 호출의 결과로 이 비동기 개체를 받습니다.
  5. 그런 다음 인스턴스의 제어점은 비동기 메서드 본문의 첫 번째 문에서 설정되고 여기에서 메서드 본문(섹션 블록 및 레이블)을 즉시 실행하기 시작합니다.

다시 시작 대리자 및 현재 호출자

Section Await 연산자에 설명된 대로 식의 Await 실행은 메서드 인스턴스의 제어점을 일시 중단하여 제어 흐름이 다른 곳으로 이동하는 기능을 줍니다. 제어 흐름은 나중에 다시 시작 대리자를 호출하여 동일한 인스턴스의 제어 지점에서 다시 시작할 수 있습니다. 이 일시 중단은 비동기 메서드를 종료하지 않고 수행되며 최종 처리기가 실행되지 않습니다. 메서드 인스턴스는 다시 시작 대리자와 Task 결과(있는 경우)에서 계속 참조되며 대리자 또는 Task(Of T) 결과에 대한 라이브 참조가 있는 한 가비지 수집되지 않습니다.

이 문 Dim x = Await WorkAsync() 은 다음과 같은 구문 약어로 상상하는 것이 유용합니다.

Dim temp = WorkAsync().GetAwaiter()
If Not temp.IsCompleted Then
       temp.OnCompleted(resumptionDelegate)
       Return [task]
       CONT:   ' invocation of 'resumptionDelegate' will resume here
End If
Dim x = temp.GetResult()

다음에서 메서드 인스턴스의 현재 호출자는 원래 호출자 또는 다시 시작 대리자의 호출자로 정의됩니다.

제어 흐름이 비동기 메서드 본문에 도달하거나 해당 끝에 도달 End Sub 하거나 End Function 명시적 Return 또는 Exit 문을 통해 또는 처리되지 않은 예외를 통해 종료되면 인스턴스의 제어점이 메서드의 끝으로 설정됩니다. 그런 다음 비동기 메서드의 반환 형식에 따라 동작이 달라집니다.

  • 반환 형식Task이 있는 Async Function 경우:

    1. 제어 흐름이 처리되지 않은 예외를 통해 종료되면 비동기 개체의 상태가 설정 TaskStatus.Faulted 되고 해당 Exception.InnerException 속성이 예외로 설정됩니다(예: 특정 구현 정의 예외(예: OperationCanceledException 변경 TaskStatus.Canceled). 제어 흐름은 현재 호출자에서 다시 시작됩니다.

    2. 그렇지 않으면 비동기 개체의 상태가 .로 TaskStatus.Completed설정됩니다. 제어 흐름은 현재 호출자에서 다시 시작됩니다.

      (참고. 작업의 전체 지점과 비동기 메서드를 흥미롭게 만드는 것은 작업이 완료될 때 대기 중인 모든 메서드가 현재 다시 시작 대리자를 실행하게 된다는 것입니다. 즉, 차단이 해제됩니다.)

  • 일부 T반환 형식 Task(Of T) 이 있는 Async Function 경우: 비 예외가 아닌 경우 비동기 개체의 속성도 작업 반환 변수의 Result 최종 값으로 설정된다는 점을 제외하고 동작은 위와 같습니다.

  • 의 경우 Async Sub:

    1. 제어 흐름이 처리되지 않은 예외를 통해 종료되는 경우 해당 예외는 구현별 방식으로 환경에 전파됩니다. 제어 흐름은 현재 호출자에서 다시 시작됩니다.
    2. 그렇지 않으면 제어 흐름이 현재 호출자에서 다시 시작됩니다.

비동기 하위

의 Microsoft 관련 동작이 있습니다 Async Sub.

SynchronizationContext.Current Nothing 호출이 시작되면 비동기 서브에서 처리되지 않은 예외가 Threadpool에 게시됩니다.

호출 OperationStarted() 이 시작되지 않은 Nothing 경우 SynchronizationContext.Current 메서드가 시작되기 전과 OperationCompleted() 종료 후에 해당 컨텍스트에서 호출됩니다. 또한 처리되지 않은 예외는 동기화 컨텍스트에서 다시 throw되도록 게시됩니다.

즉, Async Sub UI 애플리케이션에서 UI 스레드에서 호출되는 경우 처리에 실패한 예외는 UI 스레드를 다시 게시합니다.

비동기 및 반복기 메서드의 변경 가능한 구조체

일반적으로 변경 가능한 구조는 잘못된 사례로 간주되며 비동기 또는 반복기 메서드에서 지원되지 않습니다. 특히 구조체에서 비동기 또는 반복기 메서드를 호출할 때마다 호출 시 복사되는 해당 구조체의 복사본 에서 암시적으로 작동합니다. 예를 들어

Structure S
       Dim x As Integer
       Async Sub Mutate()
           x = 2
       End Sub
End Structure

Dim s As New S With {.x = 1}
s.Mutate()
Console.WriteLine(s.x)   ' prints "1"

블록 및 레이블

실행 가능한 문 그룹을 문 블록이라고 합니다. 문 블록의 실행은 블록의 첫 번째 문으로 시작합니다. 문이 실행되면 문이 다른 곳에서 실행을 전송하거나 예외가 발생하지 않는 한 어휘 순서의 다음 문이 실행됩니다.

문 블록 내에서 논리 줄의 문 나누기는 레이블 선언 문을 제외하고는 중요하지 않습니다. 레이블은 같은 분기 문의 GoTo대상으로 사용할 수 있는 문 블록 내의 특정 위치를 식별하는 식별자입니다.

Block
    : Statements*
    ;

LabelDeclarationStatement
    : LabelName ':'
    ;

LabelName
    : Identifier
    | IntLiteral
    ;

Statements
    : Statement? ( ':' Statement? )*
    ;

레이블 선언 문은 논리 줄의 시작 부분에 나타나야 하며 레이블은 식별자 또는 정수 리터럴일 수 있습니다. 레이블 선언 문과 호출 문은 모두 단일 식별자로 구성될 수 있으므로 로컬 줄의 시작 부분에 있는 단일 식별자는 항상 레이블 선언 문으로 간주됩니다. 레이블 선언 문 뒤에는 동일한 논리 줄 뒤에 문이 없더라도 항상 콜론이 와야 합니다.

레이블에는 자체 선언 공간이 있으며 다른 식별자를 방해하지 않습니다. 다음 예제는 유효하며 이름 변수 x 를 매개 변수와 레이블로 모두 사용합니다.

Function F(x As Integer) As Integer
    If x >= 0 Then
        GoTo x
    End If
    x = -x
x: 
    Return x
End Function

레이블의 범위는 레이블을 포함하는 메서드의 본문입니다.

가독성을 위해 여러 하위 상태를 포함하는 문 프로덕션은 각 하위 표현이 레이블이 지정된 줄에 있을 수 있더라도 이 사양에서 단일 프로덕션으로 처리됩니다.

지역 변수 및 매개 변수

앞의 섹션에서는 메서드 인스턴스를 만드는 방법과 시기 및 메서드의 지역 변수 및 매개 변수 복사본을 자세히 설명합니다. 또한 루프 본문이 입력될 때마다 Section Loop 문에 설명된 대로 해당 루프 내에 선언된 각 지역 변수로 새 복사본이 만들어지고, 이제 메서드 인스턴스에는 이전 복사본이 아닌 해당 지역 변수의 복사본이 포함됩니다.

모든 로컬은 해당 형식의 기본값으로 초기화됩니다. 지역 변수 및 매개 변수는 항상 공개적으로 액세스할 수 있습니다. 다음 예제와 같이 선언 앞에 있는 텍스트 위치에서 지역 변수를 참조하는 것은 오류입니다.

Class A
    Private i As Integer = 0

    Sub F()
        i = 1
        Dim i As Integer       ' Error, use precedes declaration.
        i = 2
    End Sub

    Sub G()
        Dim a As Integer = 1
        Dim b As Integer = a   ' This is valid.
    End Sub
End Class

F 위의 메서드에서 첫 번째 i 할당은 특히 외부 범위에 선언된 필드를 참조하지 않습니다. 대신 지역 변수를 참조하며 문자 그대로 변수 선언 앞에 있기 때문에 오류가 발생합니다. G 메서드에서 후속 변수 선언은 동일한 지역 변수 선언 내의 이전 변수 선언에서 선언된 지역 변수를 나타냅니다.

메서드의 각 블록은 지역 변수에 대한 선언 공간을 만듭니다. 이름은 메서드 본문의 지역 변수 선언과 가장 바깥쪽 블록의 선언 공간에 이름을 도입하는 메서드의 매개 변수 목록을 통해 이 선언 공간에 도입됩니다. 블록은 중첩을 통해 이름의 그림자를 허용하지 않습니다. 블록에서 이름이 선언되면 중첩된 블록에서 이름을 다시 선언할 수 없습니다.

따라서 다음 예제 FG 에서는 이름이 i 외부 블록에 선언되고 내부 블록에서 다시 선언할 수 없으므로 메서드와 메서드가 오류가 발생합니다. 그러나 Hi'는 별도의 중첩되지 않은 블록에 선언되어 있으므로 메서드와 I 메서드는 유효합니다.

Class A
    Sub F()
        Dim i As Integer = 0
        If True Then
               Dim i As Integer = 1
        End If
    End Sub

    Sub G()
        If True Then
            Dim i As Integer = 0
        End If
        Dim i As Integer = 1
    End Sub 

    Sub H()
        If True Then
            Dim i As Integer = 0
        End If
        If True Then
            Dim i As Integer = 1
        End If
    End Sub

    Sub I() 
        For i As Integer = 0 To 9
            H()
        Next i

        For i As Integer = 0 To 9
            H()
        Next i
    End Sub 
End Class

메서드가 함수인 경우 메서드 본문의 선언 공간에서 함수의 반환 값을 나타내는 메서드와 이름이 같은 특수 지역 변수가 암시적으로 선언됩니다. 식에 사용되는 경우 지역 변수에는 특수 이름 확인 의미 체계가 있습니다. 호출 식과 같이 메서드 그룹으로 분류된 식을 예상하는 컨텍스트에서 지역 변수를 사용하는 경우 이름은 지역 변수가 아닌 함수로 확인됩니다. 다음은 그 예입니다.

Function F(i As Integer) As Integer
    If i = 0 Then
        F = 1        ' Sets the return value.
    Else
        F = F(i - 1) ' Recursive call.
    End If
End Function

괄호를 사용하면 모호한 상황(예: F(1)F 반환 형식이 1차원 배열인 함수)이 발생할 수 있습니다. 모든 모호한 상황에서는 이름이 지역 변수가 아닌 함수로 확인됩니다. 다음은 그 예입니다.

Function F(i As Integer) As Integer()
    If i = 0 Then
        F = new Integer(2) { 1, 2, 3 }
    Else
        F = F(i - 1) ' Recursive call, not an index.
    End If
End Function

제어 흐름이 메서드 본문을 벗어나면 지역 변수의 값이 호출 식으로 다시 전달됩니다. 메서드가 서브루틴인 경우 이러한 암시적 지역 변수가 없으며 컨트롤이 호출 식으로 반환됩니다.

로컬 선언문

로컬 선언문은 새 지역 변수, 로컬 상수 또는 정적 변수를 선언합니다. 지역 변수 및로컬 상수 는 메서드로 범위가 지정된 인스턴스 변수 및 상수와 동일하며 동일한 방식으로 선언됩니다. 정적 변수는 변수와 유사 Shared 하며 한정자를 Static 사용하여 선언됩니다.

LocalDeclarationStatement
    : LocalModifier VariableDeclarators StatementTerminator
    ;

LocalModifier
    : 'Static' | 'Dim' | 'Const'
    ;

정적 변수는 메서드 호출에서 해당 값을 유지하는 지역 변수입니다. 공유되지 않은 메서드 내에서 선언된 정적 변수는 인스턴스당입니다. 메서드를 포함하는 형식의 각 인스턴스에는 정적 변수의 자체 복사본이 있습니다. 메서드 내에서 Shared 선언된 정적 변수는 형식별로, 모든 인스턴스에 대해 정적 변수의 복사본이 하나만 있습니다. 지역 변수는 메서드에 들어갈 때마다 해당 형식의 기본값으로 초기화되지만 정적 변수는 형식 또는 형식 인스턴스가 초기화될 때만 해당 형식의 기본값으로 초기화됩니다. 정적 변수는 구조체 또는 제네릭 메서드에서 선언할 수 없습니다.

지역 변수, 로컬 상수 및 정적 변수는 항상 공용 접근성을 가지며 접근성 한정자를 지정하지 않을 수 있습니다. 로컬 선언 문에 형식이 지정되지 않은 경우 다음 단계에서는 로컬 선언의 형식을 결정합니다.

  1. 선언에 형식 문자가 있는 경우 형식 문자의 형식은 로컬 선언의 형식입니다.

  2. 로컬 선언이 로컬 상수이거나 로컬 선언이 이니셜라이저가 있는 지역 변수이고 지역 변수 형식 유추가 사용되는 경우 로컬 선언의 형식은 이니셜라이저 형식에서 유추됩니다. 이니셜라이저가 로컬 선언을 참조하는 경우 컴파일 시간 오류가 발생합니다. (이니셜라이저를 사용하려면 로컬 상수가 필요합니다.)

  3. strict 의미 체계를 사용하지 않는 경우 로컬 선언 문의 형식은 암시적으로 Object사용됩니다.

  4. 그렇지 않으면 컴파일 시간 오류가 발생합니다.

배열 크기 또는 배열 형식 한정자가 있는 로컬 선언 문에 형식이 지정되지 않은 경우 로컬 선언의 형식은 지정된 순위가 있는 배열이고 이전 단계는 배열의 요소 형식을 결정하는 데 사용됩니다. 지역 변수 형식 유추를 사용하는 경우 이니셜라이저의 형식은 배열 셰이프(예: 배열 형식 한정자)가 로컬 선언문과 동일한 배열 형식이어야 합니다. 유추된 요소 형식은 여전히 배열 형식일 수 있습니다. 다음은 그 예입니다.

Option Infer On

Module Test
    Sub Main()
        ' Error: initializer is not an array type
        Dim x() = 1

        ' Type is Integer()
        Dim y() = New Integer() {}

        ' Type is Integer()()
        Dim z() = New Integer()() {}

        ' Type is Integer()()()

        Dim a()() = New Integer()()() {}

        ' Error: initializer does not have same array shape
        Dim b()() = New Integer(,)() {}
    End Sub
End Module

nullable 형식 한정자가 있는 로컬 선언 문에 형식이 지정되지 않은 경우 로컬 선언의 형식은 유추된 형식의 nullable 버전이거나 nullable 값 형식인 경우 유추된 형식 자체입니다. 유추된 형식이 null 허용으로 만들 수 있는 값 형식이 아니면 컴파일 시간 오류가 발생합니다. nullable 형식 한정자와 배열 크기 또는 배열 형식 한정자가 모두 형식이 없는 로컬 선언 문에 배치되는 경우 nullable 형식 한정자는 배열의 요소 형식에 적용되는 것으로 간주되며 이전 단계는 요소 형식을 결정하는 데 사용됩니다.

로컬 선언 문의 변수 이니셜라이저는 선언의 텍스트 위치에 배치된 할당 문과 동일합니다. 따라서 로컬 선언 문을 통해 실행 분기하는 경우 변수 이니셜라이저가 실행되지 않습니다. 로컬 선언문이 두 번 이상 실행되면 변수 이니셜라이저가 동일한 횟수만큼 실행됩니다. 정적 변수는 처음 이니셜라이저만 실행합니다. 정적 변수를 초기화하는 동안 예외가 발생하는 경우 정적 변수는 정적 변수 형식의 기본값으로 초기화된 것으로 간주됩니다.

다음 예제에서는 이니셜라이저를 사용하는 방법을 보여 줍니다.

Module Test
    Sub F()
        Static x As Integer = 5

        Console.WriteLine("Static variable x = " & x)
        x += 1
    End Sub

    Sub Main()
        Dim i As Integer

        For i = 1 to 3
            F()
        Next i

        i = 3
label:
        Dim y As Integer = 8

        If i > 0 Then
            Console.WriteLine("Local variable y = " & y)
            y -= 1
            i -= 1
            GoTo label
        End If
    End Sub
End Module

이 프로그램은 다음을 출력합니다.

Static variable x = 5
Static variable x = 6
Static variable x = 7
Local variable y = 8
Local variable y = 8
Local variable y = 8

정적 로컬의 이니셜라이저는 스레드로부터 안전하며 초기화 중에 예외로부터 보호됩니다. 정적 로컬 이니셜라이저 중에 예외가 발생하면 정적 로컬은 기본값을 가지며 초기화되지 않습니다. 정적 로컬 이니셜라이저

Module Test
    Sub F()
        Static x As Integer = 5
    End Sub
End Module

동일하다

Imports System.Threading
Imports Microsoft.VisualBasic.CompilerServices

Module Test
    Class InitFlag
        Public State As Short
    End Class

    Private xInitFlag As InitFlag = New InitFlag()

    Sub F()
        Dim x As Integer

        If xInitFlag.State <> 1 Then
            Monitor.Enter(xInitFlag)
            Try
                If xInitFlag.State = 0 Then
                    xInitFlag.State = 2
                    x = 5
                Else If xInitFlag.State = 2 Then
                    Throw New IncompleteInitialization()
                End If
            Finally
                xInitFlag.State = 1
                Monitor.Exit(xInitFlag)
            End Try
        End If
    End Sub
End Module

지역 변수, 로컬 상수 및 정적 변수는 선언된 문 블록으로 범위가 지정됩니다. 정적 변수는 해당 이름이 전체 메서드 전체에서 한 번만 사용될 수 있다는 측면에서 특별합니다. 예를 들어 서로 다른 블록에 있더라도 이름이 같은 두 정적 변수 선언을 지정하는 것은 유효하지 않습니다.

암시적 로컬 선언

로컬 선언 문 외에도 로컬 변수를 사용하여 암시적으로 선언할 수도 있습니다. 다른 이름으로 확인되지 않는 이름을 사용하는 간단한 이름 식은 해당 이름으로 지역 변수를 선언합니다. 다음은 그 예입니다.

Option Explicit Off

Module Test
    Sub Main()
        x = 10
        y = 20
        Console.WriteLine(x + y)
    End Sub
End Module

암시적 로컬 선언은 변수로 분류된 식을 수락할 수 있는 식 컨텍스트에서만 발생합니다. 이 규칙의 예외는 지역 변수가 함수 호출 식, 인덱싱 식 또는 멤버 액세스 식의 대상일 때 암시적으로 선언되지 않을 수 있다는 것입니다.

암시적 로컬은 포함하는 메서드의 시작 부분에 선언된 것처럼 처리됩니다. 따라서 람다 식 내에서 선언된 경우에도 항상 전체 메서드 본문으로 범위가 지정됩니다. 예를 들어 다음 코드는 다음과 같습니다.

Option Explicit Off 

Module Test
    Sub Main()
        Dim x = Sub()
                    a = 10
                End Sub
        Dim y = Sub()
                    Console.WriteLine(a)
                End Sub

        x()
        y()
    End Sub
End Module

은 값을 인쇄합니다 10. 암시적 지역 변수는 변수 이름에 연결된 형식 문자가 없는 것처럼 Object 형식화되고, 그렇지 않으면 변수의 형식이 형식 문자의 형식입니다. 지역 변수 형식 유추는 암시적 로컬에 사용되지 않습니다.

명시적 로컬 선언이 컴파일 환경 또는 컴파일 환경에 의해 Option Explicit지정된 경우 모든 지역 변수를 명시적으로 선언해야 하며 암시적 변수 선언은 허용되지 않습니다.

With 문

문은 With 식을 여러 번 지정하지 않고도 식의 멤버에 대한 여러 참조를 허용합니다.

WithStatement
    : 'With' Expression StatementTerminator
      Block?
      'End' 'With' StatementTerminator
    ;

식은 값으로 분류되어야 하며 블록에 들어갈 때 한 번 평가됩니다. With 문 블록 내에서 마침표 또는 느낌표로 시작하는 멤버 액세스 식 또는 사전 액세스 식은 식이 앞에 오는 것처럼 With 평가됩니다. 다음은 그 예입니다.

Structure Test
    Public x As Integer

    Function F() As Integer
        Return 10
    End Function
End Structure

Module TestModule
    Sub Main()
        Dim y As Test

        With y
            .x = 10
            Console.WriteLine(.x)
            .x = .F()
        End With
    End Sub
End Module

블록 외부에서 문 블록으로 With 분기하는 것은 유효하지 않습니다.

SyncLock 문

SyncLock 문을 사용하면 여러 실행 스레드가 동일한 문을 동시에 실행하지 않도록 식에서 문을 동기화할 수 있습니다.

SyncLockStatement
    : 'SyncLock' Expression StatementTerminator
      Block?
      'End' 'SyncLock' StatementTerminator
    ;

식은 값으로 분류되어야 하며 블록에 진입할 때 한 번 평가됩니다. 블록을 Shared 입력할 SyncLock 때 메서드 System.Threading.Monitor.Enter 는 지정된 식에서 호출되며, 이 식은 실행 스레드가 식에서 반환된 개체에 대한 배타적 잠금을 클릭하여 차단합니다. 문에 있는 SyncLock 식의 형식은 참조 형식이어야 합니다. 다음은 그 예입니다.

Class Test
    Private count As Integer = 0

    Public Function Add() As Integer
        SyncLock Me
            count += 1
            Add = count
        End SyncLock
    End Function

    Public Function Subtract() As Integer
        SyncLock Me
            count -= 1
            Subtract = count
        End SyncLock
    End Function
End Class

위의 예제는 클래스 Test 의 특정 인스턴스를 동기화하여 실행 스레드가 특정 인스턴스에 대해 한 번에 개수 변수를 추가하거나 뺄 수 없도록 합니다.

블록은 SyncLock 블록이 식에서 메서드 System.Threading.Monitor.ExitTry 호출하는 FinallyShared 문에 의해 암시적으로 포함됩니다. 이렇게 하면 예외가 throw된 경우에도 잠금이 해제됩니다. 따라서 블록 외부에서 블록으로 SyncLock 분기하는 것은 유효하지 않으며 블록은 용도 ResumeSyncLockResume Next용도에 대한 단일 문으로 처리됩니다. 위의 예제는 다음 코드와 동일합니다.

Class Test
    Private count As Integer = 0

    Public Function Add() As Integer
        Try
            System.Threading.Monitor.Enter(Me)

            count += 1
            Add = count
        Finally
            System.Threading.Monitor.Exit(Me)
        End Try
    End Function

    Public Function Subtract() As Integer
        Try
            System.Threading.Monitor.Enter(Me)

            count -= 1
            Subtract = count
        Finally
            System.Threading.Monitor.Exit(Me)
        End Try
    End Function
End Class

이벤트 문

, AddHandlerRemoveHandler 문은 RaiseEvent이벤트를 발생시키고 이벤트를 동적으로 처리합니다.

EventStatement
    : RaiseEventStatement
    | AddHandlerStatement
    | RemoveHandlerStatement
    ;

RaiseEvent 문

문은 RaiseEvent 이벤트 처리기에 특정 이벤트가 발생했음을 알 수 있습니다.

RaiseEventStatement
    : 'RaiseEvent' IdentifierOrKeyword
      ( OpenParenthesis ArgumentList? CloseParenthesis )? StatementTerminator
    ;

문의 단순 이름 식 RaiseEvent 은 멤버 조회로 해석됩니다 Me. 따라서 다음과 RaiseEvent x 같이 해석됩니다 RaiseEvent Me.x. 식의 결과는 클래스 자체에 정의된 이벤트에 대한 이벤트 액세스로 분류되어야 합니다. 기본 형식에 정의된 이벤트는 문에서 RaiseEvent 사용할 수 없습니다.

RaiseEvent 이 문은 제공된 매개 변수(있는 경우)를 사용하여 이벤트 대리자의 메서드에 대한 호출 Invoke 로 처리됩니다. 대리자의 값이 Nothing면 예외가 throw되지 않습니다. 인수가 없으면 괄호를 생략할 수 있습니다. 다음은 그 예입니다.

Class Raiser
    Public Event E1(Count As Integer)

    Public Sub Raise()
        Static RaiseCount As Integer = 0

        RaiseCount += 1
        RaiseEvent E1(RaiseCount)
    End Sub
End Class

Module Test
    Private WithEvents x As Raiser

    Private Sub E1Handler(Count As Integer) Handles x.E1
        Console.WriteLine("Raise #" & Count)
    End Sub

    Public Sub Main()
        x = New Raiser
        x.Raise()        ' Prints "Raise #1".
        x.Raise()        ' Prints "Raise #2".
        x.Raise()        ' Prints "Raise #3".
    End Sub
End Module

위의 클래스 Raiser 는 다음과 같습니다.

Class Raiser
    Public Event E1(Count As Integer)

    Public Sub Raise()
        Static RaiseCount As Integer = 0
        Dim TemporaryDelegate As E1EventHandler

        RaiseCount += 1

        ' Use a temporary to avoid a race condition.
        TemporaryDelegate = E1Event
        If Not TemporaryDelegate Is Nothing Then
            TemporaryDelegate.Invoke(RaiseCount)
        End If
    End Sub
End Class

AddHandler 및 RemoveHandler 문

대부분의 이벤트 처리기는 변수를 통해 WithEvents 자동으로 연결되지만 런타임에 이벤트 처리기를 동적으로 추가하고 제거해야 할 수 있습니다. AddHandler 문은 RemoveHandler 이 작업을 수행합니다.

AddHandlerStatement
    : 'AddHandler' Expression Comma Expression StatementTerminator
    ;

RemoveHandlerStatement
    : 'RemoveHandler' Expression Comma Expression StatementTerminator
    ;

각 문은 두 개의 인수를 사용합니다. 첫 번째 인수는 이벤트 액세스로 분류되는 식이어야 하고 두 번째 인수는 값으로 분류되는 식이어야 합니다. 두 번째 인수의 형식은 이벤트 액세스와 연결된 대리자 형식이어야 합니다. 다음은 그 예입니다.

Public Class Form1
    Public Sub New()
        ' Add Button1_Click as an event handler for Button1's Click event.
        AddHandler Button1.Click, AddressOf Button1_Click
    End Sub 

    Private Button1 As Button = New Button()

    Sub Button1_Click(sender As Object, e As EventArgs)
        Console.WriteLine("Button1 was clicked!")
    End Sub

    Public Sub Disconnect()
        RemoveHandler Button1.Click, AddressOf Button1_Click
    End Sub
End Class

이벤트가 E, 발생하면 문은 인스턴스의 관련 add_E 메서드를 remove_E 호출하여 대리자를 이벤트에 대한 처리기로 추가하거나 제거합니다. 따라서 위의 코드는 다음과 같습니다.

Public Class Form1
    Public Sub New()
        Button1.add_Click(AddressOf Button1_Click)
    End Sub 

    Private Button1 As Button = New Button()

    Sub Button1_Click(sender As Object, e As EventArgs)
        Console.WriteLine("Button1 was clicked!")
    End Sub

    Public Sub Disconnect()
        Button1.remove_Click(AddressOf Button1_Click)
    End Sub
End Class

대입문

assignment 문은 식 값을 변수에 할당합니다. 여러 가지 유형의 할당이 있습니다.

AssignmentStatement
    : RegularAssignmentStatement
    | CompoundAssignmentStatement
    | MidAssignmentStatement
    ;

일반 배정 명세서

단순 대입 문은 식의 결과를 변수에 저장합니다.

RegularAssignmentStatement
    : Expression Equals Expression StatementTerminator
    ;

대입 연산자의 왼쪽에 있는 식은 변수 또는 속성 액세스로 분류되어야 하며 할당 연산자의 오른쪽에 있는 식은 값으로 분류되어야 합니다. 식의 형식은 변수 또는 속성 액세스의 형식으로 암시적으로 변환할 수 있어야 합니다.

할당되는 변수가 참조 형식의 배열 요소인 경우 런타임 검사를 수행하여 식이 배열 요소 형식과 호환되는지 확인합니다. 다음 예제에서는 배열의 ArrayList 요소 String 에 인스턴스를 System.ArrayTypeMismatchException 저장할 수 없으므로 마지막 할당으로 인해 throw됩니다.

Dim sa(10) As String
Dim oa As Object() = sa
oa(0) = Nothing         ' This is allowed.
oa(1) = "Hello"         ' This is allowed.
oa(2) = New ArrayList() ' System.ArrayTypeMismatchException is thrown.

대입 연산자의 왼쪽에 있는 식이 변수로 분류되면 assignment 문은 변수에 값을 저장합니다. 식이 속성 액세스로 분류되는 경우 assignment 문은 속성 액세스를 값 매개 변수로 대체된 값으로 속성 접근자의 호출 Set 로 바꿉니다. 다음은 그 예입니다.

Module Test
    Private PValue As Integer

    Public Property P As Integer
        Get
            Return PValue
        End Get

        Set (Value As Integer)
            PValue = Value
        End Set
    End Property

    Sub Main()
        ' The following two lines are equivalent.
        P = 10
        set_P(10)
    End Sub
End Module

변수 또는 속성 액세스의 대상이 값 형식으로 형식화되었지만 변수로 분류되지 않은 경우 컴파일 시간 오류가 발생합니다. 다음은 그 예입니다.

Structure S
    Public F As Integer
End Structure

Class C
    Private PValue As S

    Public Property P As S
        Get
            Return PValue
        End Get

        Set (Value As S)
            PValue = Value
        End Set
    End Property
End Class

Module Test
    Sub Main()
        Dim ct As C = New C()
        Dim rt As Object = new C()

        ' Compile-time error: ct.P not classified as variable.
        ct.P.F = 10

        ' Run-time exception.
        rt.P.F = 10
    End Sub
End Module

할당의 의미 체계는 할당되는 변수 또는 속성의 형식에 따라 달라집니다. 할당되는 변수가 값 형식인 경우 할당은 식의 값을 변수에 복사합니다. 할당되는 변수가 참조 형식인 경우 할당은 값 자체가 아닌 참조를 변수에 복사합니다. 변수의 형식이 Object면 할당 의미 체계는 값 형식이 런타임에 값 형식인지 참조 형식인지에 따라 결정됩니다.

메모. 및, 참조 및 Date값 할당 의미 체계와 같은 Integer 내장 형식의 경우 형식이 변경할 수 있으므로 동일합니다. 따라서 언어는 박스형 내장 형식에 대한 참조 할당을 최적화로 자유롭게 사용할 수 있습니다. 값 관점에서 결과는 동일합니다.

같음 문자(=)는 대입과 같음 모두에 사용되므로 다음과 같은 x = y.ToString()상황에서는 단순 할당과 호출 문 사이에 모호성이 있습니다. 이러한 모든 경우에서 대입문은 같음 연산자보다 우선합니다. 즉, 예제 식은 .가 아닌 (x = y).ToString()것으로 x = (y.ToString()) 해석됩니다.

복합 할당 문

복합 할당 문은 형식 V op= E (op유효한 이진 연산자)을 사용합니다.

CompoundAssignmentStatement
    : Expression CompoundBinaryOperator LineTerminator? Expression StatementTerminator
    ;

CompoundBinaryOperator
    : '^' '=' | '*' '=' | '/' '=' | '\\' '=' | '+' '=' | '-' '='
    | '&' '=' | '<' '<' '=' | '>' '>' '='
    ;

대입 연산자의 왼쪽에 있는 식은 변수 또는 속성 액세스로 분류되어야 하고 할당 연산자의 오른쪽에 있는 식은 값으로 분류되어야 합니다. 복합 할당 문은 복합 대입 연산자의 왼쪽에 있는 변수가 한 번만 계산된다는 차이가 있는 문 V = V op E 과 동일합니다. 다음 예제에서는 이러한 차이점을 보여 줍니다.

Module Test
    Function GetIndex() As Integer
        Console.WriteLine("Getting index")
        Return 1
    End Function

    Sub Main()
        Dim a(2) As Integer

        Console.WriteLine("Simple assignment")
        a(GetIndex()) = a(GetIndex()) + 1

        Console.WriteLine("Compound assignment")
        a(GetIndex()) += 1
    End Sub
End Module

a(GetIndex()) 은 단순 할당에 대해 두 번 계산되지만 복합 할당에 대해 한 번만 평가되므로 코드가 인쇄됩니다.

Simple assignment
Getting index
Getting index
Compound assignment
Getting index

중간 배정 명세서

할당 문은 Mid 문자열을 다른 문자열에 할당합니다. 할당의 왼쪽에는 함수 Microsoft.VisualBasic.Strings.Mid에 대한 호출과 동일한 구문이 있습니다.

MidAssignmentStatement
    : 'Mid' '$'? OpenParenthesis Expression Comma Expression
      ( Comma Expression )? CloseParenthesis Equals Expression StatementTerminator
    ;

첫 번째 인수는 할당의 대상이며 형식을 암시적으로 변환할 수 String있는 변수 또는 속성 액세스로 분류해야 합니다. 두 번째 매개 변수는 대상 문자열에서 할당을 시작해야 하는 위치에 해당하며 형식을 암시적으로 변환할 수 Integer있어야 하는 값으로 분류해야 하는 1부터 시작하는 위치입니다. 선택적 세 번째 매개 변수는 대상 문자열에 할당할 오른쪽 값의 문자 수이며 형식을 암시적으로 변환할 수 Integer있는 값으로 분류해야 합니다. 오른쪽은 소스 문자열이며 형식을 암시적으로 변환할 수 String있는 값으로 분류해야 합니다. 오른쪽은 길이 매개 변수(지정된 경우)로 잘리고 시작 위치에서 시작하여 왼쪽 문자열의 문자를 바꿉니다. 오른쪽 문자열에 세 번째 매개 변수보다 적은 문자가 포함된 경우 오른쪽 문자열의 문자만 복사됩니다.

다음 예제에서는 다음을 표시합니다.ab123fg

Module Test
    Sub Main()
        Dim s1 As String = "abcdefg"
        Dim s2 As String = "1234567"

        Mid$(s1, 3, 3) = s2
        Console.WriteLine(s1)
    End Sub
End Module

메모. Mid 은 예약된 단어가 아닙니다.

호출 문

호출 문은 선택적 키워드 Call앞에 오는 메서드를 호출합니다. 호출 문은 함수 호출 식과 동일한 방식으로 처리되며 아래에 몇 가지 차이점이 있습니다. 호출 식은 값 또는 void로 분류되어야 합니다. 호출 식의 계산으로 인해 발생하는 모든 값은 삭제됩니다.

키워드를 Call 생략하면 호출 식은 식별자 또는 키워드로 시작하거나 . 블록 내에서 With 시작해야 합니다. 따라서 예를 들어 "Call 1.ToString()"는 유효한 문이지만 "1.ToString()"는 그렇지 않습니다. (식 컨텍스트에서 호출 식도 식별자를 사용하여 시작할 필요가 없습니다. 예를 들어 "Dim x = 1.ToString()"는 유효한 문입니다).

호출 문과 호출 식 간에는 또 다른 차이점이 있습니다. 호출 문에 인수 목록이 포함되어 있으면 항상 호출의 인수 목록으로 사용합니다. 다음 예제에서는 차이점을 보여 줍니다.

Module Test
    Sub Main()
        Call {Function() 15}(0)
        ' error: (0) is taken as argument list, but array is not invokable

        Call ({Function() 15}(0))
        ' valid, since the invocation statement has no argument list

        Dim x = {Function() 15}(0)
        ' valid as an expression, since (0) is taken as an array-indexing

        Call f("a")
        ' error: ("a") is taken as argument list to the invocation of f

        Call f()("a")
        ' valid, since () is the argument list for the invocation of f

        Dim y = f("a")
        ' valid as an expression, since f("a") is interpreted as f()("a")
    End Sub

    Sub f() As Func(Of String,String)
        Return Function(x) x
    End Sub
End Module
InvocationStatement
    : 'Call'? InvocationExpression StatementTerminator
    ;

조건문

조건문은 런타임에 평가되는 식을 기반으로 문을 조건부로 실행할 수 있습니다.

ConditionalStatement
    : IfStatement
    | SelectStatement
    ;

면... 그러면... Else 문

If...Then...Else 문은 기본 조건문입니다.

IfStatement
    : BlockIfStatement
    | LineIfThenStatement
    ;

BlockIfStatement
    : 'If' BooleanExpression 'Then'? StatementTerminator
      Block?
      ElseIfStatement*
      ElseStatement?
      'End' 'If' StatementTerminator
    ;

ElseIfStatement
    : ElseIf BooleanExpression 'Then'? StatementTerminator
      Block?
    ;

ElseStatement
    : 'Else' StatementTerminator
      Block?
    ;

LineIfThenStatement
    : 'If' BooleanExpression 'Then' Statements ( 'Else' Statements )? StatementTerminator
    ;
    
ElseIf		
	: 'ElseIf'		
	| 'Else' 'If'   
   ;

섹션 부울 식에 If...Then...Else 따라 문의 각 식은 부울 식이어야 합니다. (참고: 식에 부울 형식이 필요하지 않습니다.) 문의 식이 If true이면 블록으로 If 묶인 문이 실행됩니다. 식이 false이면 각 ElseIf 식이 평가됩니다. 식 중 ElseIf 하나가 true로 평가되면 해당 블록이 실행됩니다. true로 계산되는 식이 없고 블록이 Else 있으면 블록이 Else 실행됩니다. 블록 실행이 완료되면 실행이 문의 끝 If...Then...Else 으로 전달됩니다.

명령문의 If 줄 버전에는 식이 있는 경우 실행할 단일 문 집합과 식이 True 있는 경우 If 실행할 명령문의 선택적 집합이 있습니다False. 다음은 그 예입니다.

Module Test
    Sub Main()
        Dim a As Integer = 10
        Dim b As Integer = 20

        ' Block If statement.
        If a < b Then
            a = b
        Else
            b = a
        End If

        ' Line If statement
        If a < b Then a = b Else b = a
    End Sub
End Module

If 문의 줄 버전은 ":"보다 덜 단단히 바인딩되며 Else 구문에서 허용되는 사전적으로 가장 가까운 앞에 If 바인딩됩니다. 예를 들어 다음 두 버전은 동일합니다.

If True Then _
If True Then Console.WriteLine("a") Else Console.WriteLine("b") _
Else Console.WriteLine("c") : Console.WriteLine("d")

If True Then
    If True Then
        Console.WriteLine("a")
    Else
        Console.WriteLine("b")
    End If
    Console.WriteLine("c") : Console.WriteLine("d")
End If

레이블 선언 문을 제외한 모든 문은 블록 문을 포함하여 줄 If 문 내에서 허용됩니다. 그러나 여러 줄 람다 식 내부를 제외하고 LineTerminators를 StatementTerminators로 사용할 수 없습니다. 다음은 그 예입니다.

' Allowed, since it uses : instead of LineTerminator to separate statements
If b Then With New String("a"(0),5) : Console.WriteLine(.Length) : End With

' Disallowed, since it uses a LineTerminator
If b then With New String("a"(0), 5)
              Console.WriteLine(.Length)
          End With

' Allowed, since it only uses LineTerminator inside a multi-line lambda
If b Then Call Sub()
                   Console.WriteLine("a")
               End Sub.Invoke()

사례 문 선택

문은 Select Case 식 값에 따라 문을 실행합니다.

SelectStatement
    : 'Select' 'Case'? Expression StatementTerminator
      CaseStatement*
      CaseElseStatement?
      'End' 'Select' StatementTerminator
    ;

CaseStatement
    : 'Case' CaseClauses StatementTerminator
      Block?
    ;

CaseClauses
    : CaseClause ( Comma CaseClause )*
    ;

CaseClause
    : ( 'Is' LineTerminator? )? ComparisonOperator LineTerminator? Expression
    | Expression ( 'To' Expression )?
    ;

ComparisonOperator
    : '=' | '<' '>' | '<' | '>' | '>' '=' | '<' '='
    ;

CaseElseStatement
    : 'Case' 'Else' StatementTerminator
      Block?
    ;

식은 값으로 분류되어야 합니다. Select Case 문이 실행되면 Select 식이 먼저 평가되고 Case 문은 텍스트 선언 순서대로 평가됩니다. 해당 블록이 실행된 것으로 True 평가되는 첫 번째 Case 문입니다. 계산되는 True 문이 없고 Case 문이 있으면 Case Else 해당 블록이 실행됩니다. 블록 실행이 완료되면 실행이 문의 끝 Select 으로 전달됩니다.

블록의 Case 실행은 다음 스위치 섹션으로 "넘어갈" 수 없습니다. 이렇게 하면 종료 문이 실수로 생략될 때 Case 다른 언어에서 발생하는 일반적인 버그 클래스를 방지할 수 있습니다. 다음은 이 동작에 대한 예입니다.

Module Test
    Sub Main()
        Dim x As Integer = 10

        Select Case x
            Case 5
                Console.WriteLine("x = 5")
            Case 10
                Console.WriteLine("x = 10")
            Case 20 - 10
                Console.WriteLine("x = 20 - 10")
            Case 30
                Console.WriteLine("x = 30")
        End Select
    End Sub
End Module

코드는 다음을 출력합니다.

x = 10

Case 20 - 10 동일한 값 Case 10 에 대해 선택하지만 Case 10 텍스트 앞에 있기 Case 20 - 10 때문에 실행됩니다. 다음에 Case 도달하면 문 다음에 실행이 Select 계속됩니다.

절은 Case 두 가지 형식을 사용할 수 있습니다. 하나의 폼은 선택적 Is 키워드, 비교 연산자 및 식입니다. 식이 식의 형식으로 변환됩니다. 식이 식 형식 SelectSelect 으로 암시적으로 변환할 수 없는 경우 컴파일 시간 오류가 발생합니다. 식이 SelectE이면 비교 연산자는 Op이고 Case 식은 E1이면 사례는 E OP E1로 평가됩니다. 연산자는 두 식의 형식에 대해 유효해야 합니다. 그렇지 않으면 컴파일 시간 오류가 발생합니다.

다른 형식은 선택적으로 식 뒤에 키워드 To 와 두 번째 식이 옵니다. 두 식 모두 식의 Select 형식으로 변환됩니다. 두 식 중 하나가 식 형식 Select 으로 암시적으로 변환할 수 없는 경우 컴파일 시간 오류가 발생합니다. 식이 SelectE면 첫 번째 Case 식은 E1이고 두 번째 Case 식은 E = E1CaseE2지정된 식이 아니면 E2 계산됩니다(E >= E1) And (E <= E2). 연산자는 두 식의 형식에 대해 유효해야 합니다. 그렇지 않으면 컴파일 시간 오류가 발생합니다.

루프 문

루프 문을 사용하면 본문에서 문을 반복적으로 실행할 수 있습니다.

LoopStatement
    : WhileStatement
    | DoLoopStatement
    | ForStatement
    | ForEachStatement
    ;

루프 본문을 입력할 때마다 해당 본문에 선언된 모든 지역 변수로 새 복사본이 만들어지며 변수의 이전 값으로 초기화됩니다. 루프 본문 내의 변수에 대한 모든 참조는 가장 최근에 만든 복사본을 사용합니다. 이 코드는 예제를 보여줍니다.

Module Test
    Sub Main()
        Dim lambdas As New List(Of Action)
        Dim x = 1

        For i = 1 To 3
            x = i
            Dim y = x
            lambdas.Add(Sub() Console.WriteLine(x & y))
        Next

        For Each lambda In lambdas
            lambda()
        Next
    End Sub
End Module

코드는 출력을 생성합니다.

31    32    33

루프 본문이 실행되면 현재 변수의 복사본을 사용합니다. 예를 들어 이 문 Dim y = x 은 최신 복사본 y 과 원래 복사본 x을 참조합니다. 그리고 람다를 만들 때 변수가 생성되었을 때 현재 변수의 복사본을 기억합니다. 따라서 각 람다는 동일한 공유 복사본을 x사용하지만 다른 복사본 y은 사용합니다. 프로그램이 끝날 때 람다를 실행할 때 모두 참조하는 공유 복사본 x 은 이제 최종 값 3입니다.

람다 또는 LINQ 식이 없는 경우 루프 항목에서 새 복사본이 만들어지는 것을 알 수 없습니다. 실제로 컴파일러 최적화는 이 경우 복사본을 만들지 않습니다. 람다 또는 LINQ 식을 포함하는 루프에는 불법 GoTo 입니다.

동안... End While and Do... 루프 문

A While 또는 Do loop 문은 부울 식을 기반으로 반복합니다.

WhileStatement
    : 'While' BooleanExpression StatementTerminator
      Block?
      'End' 'While' StatementTerminator
    ;

DoLoopStatement
    : DoTopLoopStatement
    | DoBottomLoopStatement
    ;

DoTopLoopStatement
    : 'Do' ( WhileOrUntil BooleanExpression )? StatementTerminator
      Block?
      'Loop' StatementTerminator
    ;

DoBottomLoopStatement
    : 'Do' StatementTerminator
      Block?
      'Loop' WhileOrUntil BooleanExpression StatementTerminator
    ;

WhileOrUntil
    : 'While' | 'Until'
    ;

루프 문은 While 부울 식이 true로 평가되는 한 Do 루프를 반복합니다. 루프 문은 더 복잡한 조건을 포함할 수 있습니다. 식은 키워드 뒤 Do 또는 키워드 다음으로 배치될 수 있지만 두 키워드 뒤의 Loop 식은 배치할 수 없습니다. 부울 식은 섹션 부울 식에 따라 계산됩니다. (참고: 식에 부울 형식이 필요하지 않습니다.) 식을 전혀 지정하지 않는 것도 유효합니다. 이 경우 루프는 종료되지 않습니다. 식이 배치 Do된 경우 각 반복에서 루프 블록이 실행되기 전에 계산됩니다. 식이 배치 Loop된 경우 루프 블록이 각 반복에서 실행된 후에 평가됩니다. 따라서 식을 배치 Loop 하면 배치 후 Do보다 루프가 하나 더 생성됩니다. 다음 예제에서는 이 동작을 보여 줍니다.

Module Test
    Sub Main()
        Dim x As Integer

        x = 3
        Do While x = 1
            Console.WriteLine("First loop")
        Loop

        Do
            Console.WriteLine("Second loop")
        Loop While x = 1
    End Sub
End Module

코드는 출력을 생성합니다.

Second Loop

첫 번째 루프의 경우 루프가 실행되기 전에 조건이 평가됩니다. 두 번째 루프의 경우 루프가 실행된 후 조건이 실행됩니다. 조건식에는 키워드 또는 Until 키워드가 접두사 While 로 지정되어야 합니다. 조건이 false로 평가되면 전자는 루프를 중단하고, 조건이 true로 평가되면 후자를 중단합니다.

메모. Until 은 예약된 단어가 아닙니다.

때문에... 다음 문

For...Next 문은 범위 집합을 기반으로 반복됩니다. 문은 For 루프 제어 변수, 하한 식, 상한 식 및 선택적 단계 값 식을 지정합니다. 루프 제어 변수는 식별자 뒤에 선택적 As 절 또는 식을 통해 지정됩니다.

ForStatement
    : 'For' LoopControlVariable Equals Expression 'To' Expression
      ( 'Step' Expression )? StatementTerminator
      Block?
      ( 'Next' NextExpressionList? StatementTerminator )?
    ;

LoopControlVariable
    : Identifier ( IdentifierModifiers 'As' TypeName )?
    | Expression
    ;

NextExpressionList
    : Expression ( Comma Expression )*
    ;

다음 규칙에 따라 루프 제어 변수는 이 For...Next 문과 관련된 새 지역 변수 또는 기존 변수 또는 식을 참조합니다.

  • 루프 제어 변수가 절이 있는 As 식별자인 경우 식별자는 절에 As 지정된 형식의 새 지역 변수를 정의하며 전체 For 루프로 범위가 지정됩니다.

  • 루프 제어 변수가 절이 없는 As 식별자인 경우 식별자는 먼저 단순 이름 확인 규칙(섹션 단순 이름 식 참조)을 사용하여 확인됩니다. 단, 식별자가 발생하는 자체적으로는 암시적 지역 변수가 생성되지 않는다는 점을 제외합니다(섹션 암시적 로컬 선언).

    • 이 확인이 성공하고 결과가 변수로 분류되면 루프 제어 변수는 기존 변수입니다.

    • 해결에 실패하거나 해결이 성공하고 결과가 형식으로 분류되는 경우 다음을 수행합니다.

      • 지역 변수 형식 유추를 사용하는 경우 식별자는 바인딩 For 된 및 단계 식에서 전체 루프로 범위가 지정된 형식이 유추되는 새 지역 변수를 정의합니다.
      • 지역 변수 형식 유추가 사용되지 않지만 암시적 로컬 선언이면 범위가 전체 메서드(섹션 암시적 로컬 선언)인 암시적 지역 변수가 생성되고 루프 제어 변수는 이 기존 변수를 참조합니다.
      • 지역 변수 형식 유추나 암시적 로컬 선언이 사용되지 않으면 오류가 발생합니다.
    • 형식이나 변수로 분류되지 않은 항목으로 해결이 성공하면 오류가 발생합니다.

  • 루프 컨트롤 변수가 식인 경우 식은 변수로 분류되어야 합니다.

루프 제어 변수는 다른 묶 For...Next 는 문에서 사용할 수 없습니다. 문의 루프 제어 변수 For 형식은 반복 형식을 결정하며 다음 중 하나여야 합니다.

  • Byte, SByte, UShort, Short, UInteger, Integer, ULong, LongDecimal, SingleDouble
  • 열거형 형식
  • Object
  • T 부울 식에서 사용할 수 있는 B 형식인 다음 연산자가 있는 형식입니다.
Public Shared Operator >= (op1 As T, op2 As T) As B
Public Shared Operator <= (op1 As T, op2 As T) As B
Public Shared Operator - (op1 As T, op2 As T) As T
Public Shared Operator + (op1 As T, op2 As T) As T

바인딩된 식과 단계 식은 루프 컨트롤 변수의 형식으로 암시적으로 변환할 수 있어야 하며 값으로 분류되어야 합니다. 컴파일 시 루프 컨트롤 변수의 형식은 하한, 상한 및 단계 식 형식 중에서 가장 넓은 형식을 선택하여 유추됩니다. 두 형식 간에 확대 변환이 없으면 컴파일 시간 오류가 발생합니다.

런타임에 루프 제어 변수의 형식이 Object면 두 가지 예외를 제외하고 반복의 형식이 컴파일 시와 동일하게 유추됩니다. 첫째, 바인딩된 식과 단계 식이 모두 정수 계열 형식이지만 가장 넓은 형식이 없는 경우 세 가지 형식을 모두 포함하는 가장 넓은 형식이 유추됩니다. 둘째, 루프 제어 변수의 형식이 유추되는 StringDouble 경우 대신 유추됩니다. 런타임에 루프 컨트롤 형식을 확인할 수 없거나 식을 루프 컨트롤 형식 System.InvalidCastException 으로 변환할 수 없는 경우 발생합니다. 루프의 시작 부분에서 루프 컨트롤 형식이 선택되면 루프 컨트롤 변수의 값에 대한 변경 내용에 관계없이 반복 전체에서 동일한 형식이 사용됩니다.

For 일치하는 Next 문으로 문을 닫아야 합니다. 변수가 없는 문은 Next 가장 안쪽의 open For 문과 일치하지만 Next 하나 이상의 루프 제어 변수가 있는 문은 왼쪽에서 오른쪽으로 각 변수와 일치하는 루프와 일치 For 합니다. 변수가 해당 시점에서 가장 중첩된 루프가 아닌 루프와 일치 For 하면 컴파일 시간 오류가 발생합니다.

루프의 시작 부분에서 세 식은 텍스트 순서로 계산되고 하한 식은 루프 컨트롤 변수에 할당됩니다. 단계 값을 생략하면 루프 컨트롤 변수의 형식으로 변환되는 암시적으로 리터럴 1입니다. 세 식은 루프의 시작 부분에서만 평가됩니다.

각 루프의 시작 부분에서 제어 변수는 단계 식이 양수이면 끝점보다 크거나 단계 식이 음수이면 끝점보다 작은지 확인하기 위해 비교됩니다. 이 경우 루프가 For 종료되고, 그렇지 않으면 루프 블록이 실행됩니다. 루프 제어 변수가 기본 형식이 아닌 경우 비교 연산자는 식 step >= step - step 이 true인지 false인지에 따라 결정됩니다. 문에서 Next 단계 값은 컨트롤 변수에 추가되고 실행은 루프의 맨 위로 돌아갑니다.

루프 컨트롤 변수의 새 복사본은 루프 블록의 각 반복에 생성 되지 않습니다 . 이 점에서 문은 For (For Each섹션 For Each... 다음 문).

루프 외부에서 루프로 For 분기하는 것은 유효하지 않습니다.

For Each... 다음 문

문은 For Each...Next 식의 요소를 기반으로 반복됩니다. 문은 For Each 루프 제어 변수 및 열거자 식을 지정합니다. 루프 제어 변수는 식별자 뒤에 선택적 As 절 또는 식을 통해 지정됩니다.

ForEachStatement
    : 'For' 'Each' LoopControlVariable 'In' LineTerminator? Expression StatementTerminator
      Block?
      ( 'Next' NextExpressionList? StatementTerminator )?
    ;

문과 동일한 규칙을 For...Next 따르세요(섹션 : 다음 문) 루프 제어 변수는 이 For Each...에 특정한 새 지역 변수를 참조합니다. 다음 문 또는 기존 변수 또는 식에 대한 문입니다.

열거자 식은 값으로 분류되어야 하며 해당 형식은 컬렉션 형식 또는 Object이어야 합니다. 열거자 식의 형식이 Object면 런타임까지 모든 처리가 지연됩니다. 그렇지 않으면 컬렉션의 요소 형식에서 루프 컨트롤 변수의 형식으로 변환이 있어야 합니다.

루프 제어 변수는 다른 바깥쪽 For Each 문에서 사용할 수 없습니다. For Each 일치하는 Next 문으로 문을 닫아야 합니다. Next 루프 제어 변수가 없는 문은 가장 안쪽에 열려 For Each있는 문과 일치합니다. Next 하나 이상의 루프 컨트롤 변수가 있는 문은 왼쪽에서 오른쪽으로 동일한 루프 제어 변수가 있는 루프와 일치 For Each 합니다. 변수가 해당 시점에서 가장 중첩된 루프가 아닌 루프와 일치 For Each 하면 컴파일 시간 오류가 발생합니다.

형식 C 은 다음 중 하나인 경우 컬렉션 형식 이라고 합니다.

  • 다음은 모두 true입니다.

    • C에는 형식E을 반환하는 서명 GetEnumerator() 이 있는 액세스 가능한 인스턴스, 공유 또는 확장 메서드가 포함되어 있습니다.
    • E 에는 서명 MoveNext() 및 반환 형식 Boolean이 있는 액세스 가능한 인스턴스, 공유 또는 확장 메서드가 포함됩니다.
    • E 에는 getter가 있는 액세스 가능한 인스턴스 또는 공유 Current 속성이 포함되어 있습니다. 이 속성의 형식은 컬렉션 형식의 요소 형식입니다.
  • 인터페이스를 구현합니다. 이 경우 컬렉션의 요소 형식은 <a0/>로 간주됩니다.

  • 인터페이스를 구현합니다. 이 경우 컬렉션의 요소 형식은 <a0/>로 간주됩니다.

다음은 열거할 수 있는 클래스의 예입니다.

Public Class IntegerCollection
    Private integers(10) As Integer

    Public Class IntegerCollectionEnumerator
        Private collection As IntegerCollection
        Private index As Integer = -1

        Friend Sub New(c As IntegerCollection)
            collection = c
        End Sub

        Public Function MoveNext() As Boolean
            index += 1

            Return index <= 10
        End Function

        Public ReadOnly Property Current As Integer
            Get
                If index < 0 OrElse index > 10 Then
                    Throw New System.InvalidOperationException()
                End If

                Return collection.integers(index)
            End Get
        End Property
    End Class

    Public Sub New()
        Dim i As Integer

        For i = 0 To 10
            integers(i) = I
        Next i
    End Sub

    Public Function GetEnumerator() As IntegerCollectionEnumerator
        Return New IntegerCollectionEnumerator(Me)
    End Function
End Class

루프가 시작되기 전에 열거자 식이 계산됩니다. 식의 형식이 디자인 패턴을 충족하지 않는 경우 식이 캐스팅 System.Collections.IEnumerable 되거나 System.Collections.Generic.IEnumerable(Of T). 식 형식이 제네릭 인터페이스를 구현하는 경우 제네릭 인터페이스는 컴파일 타임에 선호되지만 런타임에 제네릭이 아닌 인터페이스를 사용하는 것이 좋습니다. 식 형식이 제네릭 인터페이스를 여러 번 구현하는 경우 문이 모호한 것으로 간주되고 컴파일 시간 오류가 발생합니다.

메모. 제네릭 인터페이스를 선택하는 것은 인터페이스 메서드에 대한 모든 호출에 형식 매개 변수가 포함된다는 것을 의미하기 때문에 런타임에 바인딩된 경우 비 제네릭 인터페이스를 사용하는 것이 좋습니다. 런타임에 일치하는 형식 인수를 알 수 없으므로 이러한 모든 호출은 런타임에 바인딩된 호출을 사용하여 수행되어야 합니다. 컴파일 시간 호출을 사용하여 제네릭이 아닌 인터페이스를 호출할 수 있으므로 제네릭이 아닌 인터페이스를 호출하는 것보다 느립니다.

GetEnumerator 는 결과 값에 대해 호출되고 함수의 반환 값은 임시로 저장됩니다. 그런 다음 각 반복 MoveNext 의 시작 부분에서 임시에서 호출됩니다. 반환 False되면 루프가 종료됩니다. 그렇지 않으면 루프의 각 반복이 다음과 같이 실행됩니다.

  1. 루프 제어 변수가 기존 변수가 아닌 새 지역 변수를 식별한 경우 이 지역 변수의 새 복사본이 만들어집니다. 현재 반복의 경우 루프 블록 내의 모든 참조가 이 복사본을 참조합니다.
  2. 속성이 Current 검색되고, 변환이 암시적인지 명시적인지 여부에 관계없이 루프 컨트롤 변수의 형식으로 강제 변환되고, 루프 제어 변수에 할당됩니다.
  3. 루프 블록이 실행됩니다.

메모. 언어 버전 10.0과 11.0 사이에 약간의 동작이 변경되었습니다. 11.0 이전에는 루프의 각 반복에 대해 새 반복 변수가 만들어 지지 않았습니다 . 이 차이는 반복 변수가 람다 또는 LINQ 식에 의해 캡처된 다음 루프 후에 호출되는 경우에만 관찰할 수 있습니다.

Dim lambdas As New List(Of Action)
For Each x In {1,2,3}
   lambdas.Add(Sub() Console.WriteLine(x)
Next
lambdas(0).Invoke()
lambdas(1).Invoke()
lambdas(2).Invoke()

Visual Basic 10.0까지 컴파일 시간에 경고를 생성하고 "3"을 세 번 인쇄했습니다. 이는 루프의 모든 반복에서 공유되는 단일 변수 "x"만 있었고 세 개의 람다 모두 동일한 "x"를 캡처하고 람다가 실행될 때까지 숫자 3을 유지했기 때문입니다. Visual Basic 11.0을 기준으로 "1, 2, 3"을 인쇄합니다. 각 람다는 다른 변수 "x"를 캡처하기 때문입니다.

메모. 문에 변환 연산자를 도입할 수 있는 편리한 위치가 없기 때문에 변환이 명시적이더라도 반복의 현재 요소는 루프 컨트롤 변수의 형식으로 변환됩니다. 이제 사용되지 않는 형식으로 작업할 때 요소 형식System.Collections.ArrayListObject이 .이므로 이 문제는 특히 번거로웠습니다. 이것은 우리가 이상적이지 않다고 느꼈던 많은 루프에 캐스트가 필요했을 것입니다. 아이러니하게도 제네릭은 강력한 형식의 컬렉션을 System.Collections.Generic.List(Of T)만들 수 있게 하여 이 디자인 포인트를 재고하게 만들었을 지 모르지만, 호환성을 위해 지금은 변경할 수 없습니다.

문에 Next 도달하면 실행이 루프의 맨 위로 돌아갑니다. 키워드 다음에 변수를 Next 지정하는 경우 변수는 다음의 첫 번째 변수 For Each와 동일해야 합니다. 예를 들어 다음 코드를 고려합니다.

Module Test
    Sub Main()
        Dim i As Integer
        Dim c As IntegerCollection = New IntegerCollection()

        For Each i In c
            Console.WriteLine(i)
        Next i
    End Sub
End Module

다음 코드와 동일합니다.

Module Test
    Sub Main()
        Dim i As Integer
        Dim c As IntegerCollection = New IntegerCollection()

        Dim e As IntegerCollection.IntegerCollectionEnumerator

        e = c.GetEnumerator()
        While e.MoveNext()
            i = e.Current

            Console.WriteLine(i)
        End While
    End Sub
End Module

열거자의 형식 E 이 구현 System.IDisposable되는 경우 메서드를 호출 Dispose 하여 루프를 종료할 때 열거자가 삭제됩니다. 이렇게 하면 열거자가 보유한 리소스가 해제됩니다. 문이 포함된 For Each 메서드가 구조화되지 않은 오류 처리를 For Each 사용하지 않는 경우 문은 정리를 보장하기 위해 호출된 메서드를 사용하여 Dispose 문에 Finally 래핑 Try 됩니다.

메모. 이 형식은 System.Array 컬렉션 형식이며 모든 배열 형식이 System.Array파생되므로 문에서 For Each 모든 배열 형식 식이 허용됩니다. 1차원 배열의 For Each 경우 이 문은 인덱스 0부터 시작하여 인덱스 길이 - 1로 끝나는 인덱스 순서로 배열 요소를 열거합니다. 다차원 배열의 경우 가장 오른쪽 차원의 인덱스가 먼저 증가합니다.

예를 들어 다음 코드는 다음을 출력합니다.1 2 3 4

Module Test
    Sub Main()
        Dim x(,) As Integer = { { 1, 2 }, { 3, 4 } }
        Dim i As Integer

        For Each i In x
            Console.Write(i & " ")
        Next i
    End Sub
End Module

블록 외부에서 문 블록으로 For Each 분기하는 것은 유효하지 않습니다.

Exception-Handling 문

Visual Basic은 구조적 예외 처리 및 구조화되지 않은 예외 처리를 지원합니다. 메서드에서 예외 처리 스타일을 하나만 사용할 수 있지만 이 문은 Error 구조적 예외 처리에 사용될 수 있습니다. 메서드가 예외 처리의 두 스타일을 모두 사용하는 경우 컴파일 시간 오류가 발생합니다.

ErrorHandlingStatement
    : StructuredErrorStatement
    | UnstructuredErrorStatement
    ;

구조적 Exception-Handling 문

구조적 예외 처리는 특정 예외가 처리될 명시적 블록을 선언하여 오류를 처리하는 방법입니다. 구조적 예외 처리는 문을 통해 Try 수행됩니다.

StructuredErrorStatement
    : ThrowStatement
    | TryStatement
    ;

TryStatement
    : 'Try' StatementTerminator
      Block?
      CatchStatement*
      FinallyStatement?
      'End' 'Try' StatementTerminator
    ;

다음은 그 예입니다.

Module Test
    Sub ThrowException()
        Throw New Exception()
    End Sub

    Sub Main()
        Try
            ThrowException()
        Catch e As Exception
            Console.WriteLine("Caught exception!")
        Finally
            Console.WriteLine("Exiting try.")
        End Try
    End Sub
End Module

Try 문은 try 블록, catch 블록 및 finally 블록의 세 가지 종류의 블록으로 구성됩니다. try 블록은 실행할 문을 포함하는 문 블록입니다. catch 블록은 예외를 처리하는 문 블록입니다. 마지막 블록은 예외가 발생하고 처리되었는지 여부에 관계없이 문이 종료될 때 Try 실행할 문을 포함하는 문 블록입니다. Try try 블록 하나와 최종 블록 하나만 포함할 수 있는 문은 하나 이상의 catch 블록을 포함하거나 마지막으로 차단해야 합니다. 동일한 문의 catch 블록 내에서 실행을 명시적으로 try 블록으로 전송하는 것은 유효하지 않습니다.

최종적으로 블록

Finally 블록은 실행이 Try 문의 일부를 벗어날 때 항상 실행됩니다. 블록을 실행하는 Finally 데 명시적 작업이 필요하지 않습니다. 실행이 문을 떠날 Try 때 시스템은 자동으로 블록을 실행 Finally 한 다음 실행을 의도한 대상으로 전송합니다. 블록은 Finally 블록의 끝을 통해, 블록의 Try 끝을 Catch 통해, 문을 통해, 문을 통해 Exit TryGoTo 또는 throw된 예외를 처리하지 않음으로써 실행을 종료 Try 하는 방법에 관계없이 실행됩니다.

비동기 메서드의 Await 식과 Yield 반복기 메서드의 문으로 인해 비동기 또는 반복기 메서드 인스턴스에서 제어 흐름이 일시 중단되고 다른 메서드 인스턴스에서 다시 시작될 수 있습니다. 그러나 이는 단지 실행 일시 중단일 뿐이며 해당 비동기 메서드 또는 반복기 메서드 인스턴스를 종료하지 않으므로 블록이 실행되지 않습니다 Finally .

명시적으로 실행을 블록으로 Finally 전송하는 것은 유효하지 않습니다. 예외를 제외하고 블록에서 Finally 실행을 전송하는 것도 유효하지 않습니다.

FinallyStatement
    : 'Finally' StatementTerminator
      Block?
    ;

Catch 블록

블록을 처리하는 Try 동안 예외가 발생하면 각 Catch 문이 텍스트 순서로 검사되어 예외를 처리하는지 확인합니다.

CatchStatement
    : 'Catch' ( Identifier ( 'As' NonArrayTypeName )? )?
	  ( 'When' BooleanExpression )? StatementTerminator
      Block?
    ;

절에 Catch 지정된 식별자는 throw된 예외를 나타냅니다. 식별자에 절이 As 포함된 경우 식별자는 블록의 로컬 선언 공간 내에서 Catch 선언된 것으로 간주됩니다. 그렇지 않으면 식별자는 포함하는 블록에 정의된 지역 변수(정적 변수가 아님)여야 합니다.

Catch 식별자가 없는 절은 에서 System.Exception파생된 모든 예외를 catch합니다. Catch 식별자가 있는 절은 형식이 식별자 형식과 동일하거나 파생된 예외만 catch합니다. 형식은 .에서 System.Exception파생된 형식이어야 합니다System.Exception. 파생 System.Exception되는 예외가 catch되면 예외 개체에 대한 참조가 함수 Microsoft.VisualBasic.Information.Err에서 반환된 개체에 저장됩니다.

Catch 절이 있는 When 절은 식이 계산될 때만 예외를 Truecatch합니다. 식의 형식은 Section Boolean 식에 따라 부울 식이어야 합니다. When 절은 예외의 형식을 확인한 후에만 적용되며, 이 예제와 같이 식은 예외를 나타내는 식별자를 참조할 수 있습니다.

Module Test
    Sub Main()
        Dim i As Integer = 5

        Try
            Throw New ArgumentException()
        Catch e As OverflowException When i = 5
            Console.WriteLine("First handler")
        Catch e As ArgumentException When i = 4
            Console.WriteLine("Second handler")
        Catch When i = 5
            Console.WriteLine("Third handler")
        End Try

    End Sub
End Module

다음은 다음을 인쇄하는 예제입니다.

Third handler

절에서 Catch 예외를 처리하면 실행이 블록으로 Catch 전송됩니다. 블록의 Catch 끝에서 실행은 문 다음의 첫 번째 문으로 Try 전송됩니다. 이 문은 Try 블록에서 Catch throw된 예외를 처리하지 않습니다. 예외를 처리하는 절이 없 Catch 으면 실행은 시스템에 의해 결정된 위치로 전송됩니다.

실행을 블록으로 Catch 명시적으로 전송하는 것은 유효하지 않습니다.

When 절의 필터는 일반적으로 예외가 throw되기 전에 평가됩니다. 예를 들어 다음 코드는 "Filter, Finally, Catch"를 출력합니다.

Sub Main()
   Try
       Foo()
   Catch ex As Exception When F()
       Console.WriteLine("Catch")
   End Try
End Sub

Sub Foo()
    Try
        Throw New Exception
    Finally
        Console.WriteLine("Finally")
    End Try
End Sub

Function F() As Boolean
    Console.WriteLine("Filter")
    Return True
End Function

그러나 비동기 및 반복기 메서드를 사용하면 외부의 필터가 실행되기 전에 내부 블록이 모두 실행됩니다. 예를 들어 위의 코드가 있는 Async Sub Foo()경우 출력은 "Finally, Filter, Catch"입니다.

Throw 문

이 문은 Throw 예외를 발생시키는데, 이 예외는 파생된 형식의 인스턴스로 System.Exception표시됩니다.

ThrowStatement
    : 'Throw' Expression? StatementTerminator
    ;

식이 값으로 분류되지 않았거나 파생된 System.Exception형식이 아니면 컴파일 시간 오류가 발생합니다. 식이 런타임 System.NullReferenceException 에 null 값으로 계산되면 대신 예외가 발생합니다.

문은 Throw 최종 블록이 개입되지 않는 한 Try 명령문의 catch 블록 내에서 식을 생략할 수 있습니다. 이 경우 문은 현재 catch 블록 내에서 처리 중인 예외를 다시 throw합니다. 다음은 그 예입니다.

Sub Test(x As Integer)
    Try
        Throw New Exception()
    Catch
        If x = 0 Then
            Throw    ' OK, rethrows exception from above.
        Else
            Try
                If x = 1 Then
                    Throw   ' OK, rethrows exception from above.
                End If
            Finally
                Throw    ' Invalid, inside of a Finally.
            End Try
        End If
    End Try
End Sub

구조화되지 않은 Exception-Handling 문

구조화되지 않은 예외 처리는 예외가 발생할 때 분기할 문을 표시하여 오류를 처리하는 방법입니다. 구조화되지 않은 예외 처리는 문, On Error 문 및 Resume 문의 세 가지 Error 문을 사용하여 구현됩니다.

UnstructuredErrorStatement
    : ErrorStatement
    | OnErrorStatement
    | ResumeStatement
    ;

다음은 그 예입니다.

Module Test
    Sub ThrowException()
        Error 5
    End Sub

    Sub Main()
        On Error GoTo GotException

        ThrowException()
        Exit Sub

GotException:
        Console.WriteLine("Caught exception!")
        Resume Next
    End Sub
End Module

메서드가 구조화되지 않은 예외 처리를 사용하는 경우 모든 예외를 catch하는 전체 메서드에 대해 단일 구조적 예외 처리기가 설정됩니다. (생성자에서 이 처리기는 생성자의 시작 부분에 있는 호출에 New 대한 호출을 통해 확장되지 않습니다.) 그런 다음 메서드는 가장 최근의 예외 처리기 위치와 throw된 가장 최근의 예외를 추적합니다. 메서드를 입력할 때 예외 처리기 위치와 예외는 모두 로 Nothing설정됩니다. 구조화되지 않은 예외 처리를 사용하는 메서드에서 예외가 throw되면 예외 개체에 대한 참조가 함수 Microsoft.VisualBasic.Information.Err에서 반환된 개체에 저장됩니다.

비정형 오류 처리 문은 반복기 또는 비동기 메서드에서 허용되지 않습니다.

Error 문

Error 문이 Visual Basic 6 예외 번호를 포함하는 예외를 throw System.Exception 합니다. 식은 값으로 분류되어야 하며 해당 형식은 암시적으로 변환할 Integer수 있어야 합니다.

ErrorStatement
    : 'Error' Expression StatementTerminator
    ;

On Error 문

문은 On Error 가장 최근의 예외 처리 상태를 수정합니다.

OnErrorStatement
    : 'On' 'Error' ErrorClause StatementTerminator
    ;

ErrorClause
    : 'GoTo' '-' '1'
    | 'GoTo' '0'
    | GoToStatement
    | 'Resume' 'Next'
    ;

다음 네 가지 방법 중 하나로 사용할 수 있습니다.

  • On Error GoTo -1 에서 가장 최근 예외를 다시 설정합니다 Nothing.

  • On Error GoTo 0 에서 가장 최근의 예외 처리기 위치를 Nothing다시 설정합니다.

  • On Error GoTo LabelName 는 레이블을 가장 최근의 예외 처리기 위치로 설정합니다. 이 문은 람다 또는 쿼리 식을 포함하는 메서드에서 사용할 수 없습니다.

  • On Error Resume Next 는 가장 Resume Next 최근의 예외 처리기 위치로 동작을 설정합니다.

Resume 문

문은 Resume 가장 최근의 예외를 발생시킨 문으로 실행을 반환합니다.

ResumeStatement
    : 'Resume' ResumeClause? StatementTerminator
    ;

ResumeClause
    : 'Next'
    | LabelName
    ;

Next 한정자를 지정하면 실행은 가장 최근의 예외를 발생시킨 문 다음에 실행되었을 문으로 돌아갑니다. 레이블 이름을 지정하면 실행이 레이블로 돌아갑니다.

문에는 SyncLock 암시적 구조적 오류 처리 블록이 포함되어 있고 Resume NextResume 에서 SyncLock 발생하는 예외에 대한 특별한 동작이 있기 때문입니다. Resume 는 명령문의 SyncLock 시작 부분으로 실행을 반환하고 Resume Next 명령문 다음에 오는 다음 문으로 실행을 반환합니다 SyncLock . 예를 들어 다음 코드를 고려합니다.

Class LockClass
End Class

Module Test
    Sub Main()
        Dim FirstTime As Boolean = True
        Dim Lock As LockClass = New LockClass()

        On Error GoTo Handler

        SyncLock Lock
            Console.WriteLine("Before exception")
            Throw New Exception()
            Console.WriteLine("After exception")
        End SyncLock

        Console.WriteLine("After SyncLock")
        Exit Sub

Handler:
        If FirstTime Then
            FirstTime = False
            Resume
        Else
            Resume Next
        End If
    End Sub
End Module

다음 결과를 출력합니다.

Before exception
Before exception
After SyncLock

문을 통해 처음으로 SyncLock 실행이 문의 시작 부분으로 반환됩니다SyncLock. Resume 문을 Resume Next 통해 SyncLock 두 번째로 실행이 문 끝에 반환됩니다SyncLock. ResumeResume Next 내에서 SyncLock 허용되지 않습니다.

모든 경우에 문이 실행될 때 Resume 가장 최근의 예외는 .로 Nothing설정됩니다. Resume 가장 최근의 예외 없이 문을 실행하면 Visual Basic 오류 번호 20 (오류 없이 다시 시작)가 포함된 예외가 발생 System.Exception 합니다.

Branch 문

분기 문은 메서드의 실행 흐름을 수정합니다. 분기 문은 6개입니다.

  1. GoTo 문을 사용하면 실행이 메서드의 지정된 레이블로 전송됩니다. 해당 블록의 지역 변수가 람다 또는 LINQ 식에서 캡처되는 경우 루프 블록이나 루프 블록에 포함 WithForSyncLockTryUsing할 수 GoTo 없습니다.For Each
  2. 문은 Exit 지정된 종류의 블록 문이 포함된 즉시 종료된 후 다음 문으로 실행을 전송합니다. 블록이 메서드 블록인 경우 제어 흐름은 섹션 제어 흐름에 설명된 대로 메서드를 종료합니다. 문에 Exit 지정된 블록 종류 내에 문이 포함되어 있지 않으면 컴파일 시간 오류가 발생합니다.
  3. 문은 Continue 지정된 종류의 블록 루프 문을 포함하는 즉시의 끝으로 실행을 전송합니다. 문에 Continue 지정된 블록 종류 내에 문이 포함되어 있지 않으면 컴파일 시간 오류가 발생합니다.
  4. Stop 문을 사용하면 디버거 예외가 발생합니다.
  5. End 문이 프로그램을 종료합니다. 종료자는 종료 전에 실행되지만 현재 실행 중인 Try 문의 최종 블록은 실행되지 않습니다. 이 문은 실행 가능하지 않은 프로그램(예: DLL)에서 사용할 수 없습니다.
  6. Return 식이 없는 문은 또는 Exit Function 문과 Exit Sub 동일합니다. Return 식이 있는 문은 함수인 일반 메서드 또는 일부 T함수에 대한 반환 형식 Task(Of T) 이 있는 함수인 비동기 메서드에서만 허용됩니다. 식은 함수 반환 변수(일반 메서드의 경우) 또는 작업 반환 변수 (비동기 메서드의 경우)로 암시적으로 변환할 수 있는 으로 분류되어야 합니다. 해당 동작은 식을 계산한 다음 반환 변수에 저장한 다음 암시적 Exit Function 문을 실행하는 것입니다.
BranchStatement
    : GoToStatement
    | ExitStatement
    | ContinueStatement
    | StopStatement
    | EndStatement
    | ReturnStatement
    ;

GoToStatement
    : 'GoTo' LabelName StatementTerminator
    ;

ExitStatement
    : 'Exit' ExitKind StatementTerminator
    ;

ExitKind
    : 'Do' | 'For' | 'While' | 'Select' | 'Sub' | 'Function' | 'Property' | 'Try'
    ;

ContinueStatement
    : 'Continue' ContinueKind StatementTerminator
    ;

ContinueKind
    : 'Do' | 'For' | 'While'
    ;

StopStatement
    : 'Stop' StatementTerminator
    ;

EndStatement
    : 'End' StatementTerminator
    ;

ReturnStatement
    : 'Return' Expression? StatementTerminator
    ;

Array-Handling 문

두 문은 문 및 Erase 문과 같은 배열 ReDim 작업을 간소화합니다.

ArrayHandlingStatement
    : RedimStatement
    | EraseStatement
    ;

ReDim 문

문은 ReDim 새 배열을 인스턴스화합니다.

RedimStatement
    : 'ReDim' 'Preserve'? RedimClauses StatementTerminator
    ;

RedimClauses
    : RedimClause ( Comma RedimClause )*
    ;

RedimClause
    : Expression ArraySizeInitializationModifier
    ;

문의 각 절은 해당 형식이 배열 형식이거나 배열 범위 목록 뒤에 있는 변수 또는 Object속성 액세스로 분류되어야 합니다. 범위 수는 변수의 형식과 일치해야 합니다. 에 대해 허용되는 범위의 Object수는 없습니다. 런타임에 지정된 범위를 사용하여 왼쪽에서 오른쪽으로 각 식에 대해 배열을 인스턴스화한 다음 변수 또는 속성에 할당됩니다. 변수 형식이 Object면 차원 수는 지정된 차원의 수이고 배열 요소 형식은 다음과 같습니다 Object. 지정된 차원 수가 런타임에 변수 또는 속성과 호환되지 않는 경우 컴파일 시간 오류가 발생합니다. 다음은 그 예입니다.

Module Test
    Sub Main()
        Dim o As Object
        Dim b() As Byte
        Dim i(,) As Integer

        ' The next two statements are equivalent.
        ReDim o(10,30)
        o = New Object(10, 30) {}

        ' The next two statements are equivalent.
        ReDim b(10)
        b = New Byte(10) {}

        ' Error: Incorrect number of dimensions.
        ReDim i(10, 30, 40)
    End Sub
End Module

키워드를 Preserve 지정하면 식도 값으로 분류할 수 있어야 하며, 오른쪽을 제외한 각 차원의 새 크기는 기존 배열의 크기와 동일해야 합니다. 기존 배열의 값은 새 배열에 복사됩니다. 새 배열이 작으면 기존 값이 삭제됩니다. 새 배열이 더 크면 추가 요소가 배열의 요소 형식의 기본값으로 초기화됩니다. 예를 들어 다음 코드를 고려합니다.

Module Test
    Sub Main()
        Dim x(5, 5) As Integer

        x(3, 3) = 3

        ReDim Preserve x(5, 6)
        Console.WriteLine(x(3, 3) & ", " & x(3, 6))
    End Sub
End Module

다음 결과를 출력합니다.

3, 0

기존 배열 참조가 런타임에 null 값인 경우 오류가 발생하지 않습니다. 가장 오른쪽 차원이 아닌 차원의 크기가 변경되면 throw System.ArrayTypeMismatchException 됩니다.

메모. Preserve 은 예약된 단어가 아닙니다.

Erase 문

문은 EraseNothing에 지정된 각 배열 변수 또는 속성을 .로 설정합니다. 문의 각 식은 해당 형식이 배열 형식 Object인 변수 또는 속성 액세스로 분류되어야 합니다. 다음은 그 예입니다.

Module Test
    Sub Main()
        Dim x() As Integer = New Integer(5) {}

        ' The following two statements are equivalent.
        Erase x
        x = Nothing
    End Sub
End Module
EraseStatement
    : 'Erase' EraseExpressions StatementTerminator
    ;

EraseExpressions
    : Expression ( Comma Expression )*
    ;

Using 문

형식의 인스턴스는 컬렉션이 실행되고 인스턴스에 대한 라이브 참조를 찾을 수 없을 때 가비지 수집기에서 자동으로 해제됩니다. 형식이 특히 중요하고 부족한 리소스(예: 데이터베이스 연결 또는 파일 핸들)를 보유하는 경우 다음 가비지 수집이 더 이상 사용되지 않는 형식의 특정 인스턴스를 정리할 때까지 기다리는 것이 바람직하지 않을 수 있습니다. 컬렉션 전에 리소스를 해제하는 간단한 방법을 제공하기 위해 형식이 인터페이스를 구현할 System.IDisposable 수 있습니다. 이렇게 하는 형식은 다음과 같이 중요한 리소스를 즉시 해제하도록 호출할 수 있는 메서드를 노출 Dispose 합니다.

Module Test
    Sub Main()
        Dim x As DBConnection = New DBConnection("...")

        ' Do some work
        ...

        x.Dispose()        ' Free the connection
    End Sub
End Module

이 문은 Using 리소스를 획득하고, 문 집합을 실행한 다음, 리소스를 삭제하는 프로세스를 자동화합니다. 문은 두 가지 형식을 사용할 수 있습니다. 즉, 리소스는 문의 일부로 선언되고 일반 지역 변수 선언문으로 처리되는 지역 변수입니다. 반면에 리소스는 식의 결과입니다.

UsingStatement
    : 'Using' UsingResources StatementTerminator
      Block?
      'End' 'Using' StatementTerminator
    ;

UsingResources
    : VariableDeclarators
    | Expression
    ;

리소스가 지역 변수 선언문인 경우 지역 변수 선언의 형식은 암시적으로 변환 System.IDisposable할 수 있는 형식이어야 합니다. 선언된 지역 변수는 읽기 전용이며 문 블록으로 범위가 Using 지정되며 이니셜라이저를 포함해야 합니다. 리소스가 식의 결과인 경우 식은 값으로 분류되어야 하며 암시적으로 변환 System.IDisposable할 수 있는 형식이어야 합니다. 식은 문의 시작 부분에서 한 번만 계산됩니다.

블록은 Using 마지막으로 블록이 리소스에서 메서드 IDisposable.DisposeTry 호출하는 문에 의해 암시적으로 포함됩니다. 이렇게 하면 예외가 throw된 경우에도 리소스가 삭제됩니다. 따라서 블록 외부에서 블록으로 Using 분기하는 것은 유효하지 않으며 블록은 용도 ResumeUsingResume Next용도에 대한 단일 문으로 처리됩니다. 리소스가 Nothing있으면 호출 Dispose 되지 않습니다. 따라서 예제는 다음과 같습니다.

Using f As C = New C()
    ...
End Using

다음과 동일합니다.

Dim f As C = New C()
Try
    ...
Finally
    If f IsNot Nothing Then
        f.Dispose()
    End If
End Try

Using 지역 변수 선언문이 있는 문은 한 번에 여러 리소스를 획득할 수 있으며 이는 중첩된 Using 문과 동일합니다. 예를 들어 Using 양식의 문은 다음과 같습니다.

Using r1 As R = New R(), r2 As R = New R()
    r1.F()
    r2.F()
End Using

다음과 동일합니다.

Using r1 As R = New R()
    Using r2 As R = New R()
        r1.F()
        r2.F()
    End Using
End Using

Await 문

await 문은 await 연산자 식(Section Await 연산자)과 동일한 구문을 가지며 await 식을 허용하는 메서드에서만 허용되며 await 연산자 식과 동일한 동작을 가집니다.

그러나 값 또는 void로 분류될 수 있습니다. await 연산자 식의 계산으로 인해 발생하는 모든 값은 삭제됩니다.

AwaitStatement
    : AwaitOperatorExpression StatementTerminator
    ;

Yield 문

Yield 문은 Section 반복기 메서드에 설명된 반복 기 메서드와 관련이 있습니다.

YieldStatement
    : 'Yield' Expression StatementTerminator
    ;

Yield는 표시되는 즉시 바깥쪽 메서드 또는 람다 식에 Iterator 한정자가 있고 해당 Iterator 한정자 이후에 나타나는 경우 Yield 예약된 단어입니다. 다른 곳에서는 예약되지 않습니다. 전처리기 지시문에서도 예약되지 않습니다. yield 문은 예약어인 메서드 또는 람다 식의 본문에서만 허용됩니다. 즉시 바깥쪽 메서드 또는 람다 내에서 yield 문은 또는 블록의 Catch 본문 내부 또는 Finally 문 본문 SyncLock 내에서 발생하지 않을 수 있습니다.

yield 문은 값으로 분류되어야 하고 해당 형식이 바깥쪽 반복기 메서드의 반복기 현재 변수 (Section Iterator Methods)의 형식으로 암시적으로 변환할 수 있는 단일 식을 사용합니다.

제어 흐름은 반복기 개체에서 메서드가 MoveNext 호출될 때만 문에 도달 Yield 합니다. 반복기 메서드 인스턴스가 반복기 개체에서 호출되는 메서드 또는 Dispose 메서드로 인해 해당 문만 실행하기 때문 MoveNext 이며Dispose, 메서드는 허용되지 않는 블록 Yield 에서 Finally 만 코드를 실행하기 때문입니다.

Yield 문이 실행되면 해당 식이 평가되고 해당 반복기 개체와 연결된 반복기 메서드 인스턴스의 반복기 현재 변수에 저장됩니다. 값 True 은 호출자에 MoveNext반환되고 이 인스턴스의 제어점은 반복기 개체에서 MoveNext 다음 호출될 때까지 진행을 중지합니다.