확장 메서드(Visual Basic)

확장 메서드를 사용하면 개발자가 새 파생 형식을 만들지 않고 이미 정의된 데이터 형식에 사용자 지정 기능을 추가할 수 있습니다. 확장 메서드를 사용하면 기존 형식의 인스턴스 메서드인 것처럼 호출할 수 있는 메서드를 작성할 수 있습니다.

설명

확장 메서드는 프로시저 또는 Function 프로시저일 수 Sub 있습니다. 확장 속성, 필드 또는 이벤트를 정의할 수 없습니다. 모든 확장 메서드는 네임스페이스의 확장 특성 <Extension>System.Runtime.CompilerServices 으로 표시되어야 하며 모듈에서 정의해야 합니다. 확장 메서드가 모듈 외부에서 정의되면 Visual Basic 컴파일러는 "확장 메서드는 모듈에서만 정의할 수 있습니다."라는 오류 BC36551을 생성합니다.

확장 메서드 정의의 첫 번째 매개 변수는 메서드가 확장하는 데이터 형식을 지정합니다. 메서드가 실행되면 첫 번째 매개 변수는 메서드를 호출하는 데이터 형식의 인스턴스에 바인딩됩니다.

특성은 Extension Visual Basic ModuleSubFunction또는 에만 적용할 수 있습니다. a 또는 a ClassStructure에 적용하는 경우 Visual Basic 컴파일러는 BC36550 오류를 생성합니다. "'Extension' 특성은 'Module', 'Sub' 또는 'Function' 선언에만 적용할 수 있습니다."

예제

다음 예제에서는 데이터 형식에 대한 PrintString 확장을 정의합니다. 메서드는 문자열을 표시하는 데 사용합니다 Console.WriteLine . 메서드의 Print 매개 변수는 aString메서드가 클래스를 확장한다는 String 것을 설정합니다.

Imports System.Runtime.CompilerServices

Module StringExtensions

    <Extension()> 
    Public Sub Print(ByVal aString As String)
        Console.WriteLine(aString)
    End Sub

End Module

확장 메서드 정의는 확장 특성 <Extension()>으로 표시됩니다. 메서드가 정의된 모듈을 표시하는 것은 선택 사항이지만 각 확장 메서드를 표시해야 합니다. System.Runtime.CompilerServices 확장 특성에 액세스하려면 가져와야 합니다.

확장 메서드는 모듈 내에서만 선언할 수 있습니다. 일반적으로 확장 메서드가 정의된 모듈은 호출되는 모듈과 동일하지 않습니다. 대신 확장 메서드를 포함하는 모듈을 가져와야 하는 경우 범위로 가져옵니다. 포함된 Print 모듈이 범위에 있으면 다음과 같이 ToUpper인수를 사용하지 않는 일반 인스턴스 메서드인 것처럼 메서드를 호출할 수 있습니다.

Module Class1

    Sub Main()

        Dim example As String = "Hello"
        ' Call to extension method Print.
        example.Print()

        ' Call to instance method ToUpper.
        example.ToUpper()
        example.ToUpper.Print()

    End Sub

End Module

다음 예제인 PrintAndPunctuate이때 두 개의 매개 변수로 String정의된 확장도 있습니다. 첫 번째 매개 변수는 aString확장 메서드가 확장되도록 String설정합니다. 두 번째 매개 변수 punc는 메서드가 호출될 때 인수로 전달되는 문장 부호 문자열입니다. 메서드는 문자열 뒤에 문장 부호를 표시합니다.

<Extension()> 
Public Sub PrintAndPunctuate(ByVal aString As String, 
                             ByVal punc As String)
    Console.WriteLine(aString & punc)
End Sub

메서드는 다음의 문자열 인수를 전송하여 호출됩니다 punc. example.PrintAndPunctuate(".")

다음 예제에서는 정의 PrintPrintAndPunctuate 호출합니다. System.Runtime.CompilerServices 확장 특성에 대한 액세스를 사용하도록 설정하기 위해 정의 모듈에서 가져옵니다.

Imports System.Runtime.CompilerServices

Module StringExtensions

    <Extension()>
    Public Sub Print(aString As String)
        Console.WriteLine(aString)
    End Sub

    <Extension()>
    Public Sub PrintAndPunctuate(aString As String, punc As String)
        Console.WriteLine(aString & punc)
    End Sub
End Module

다음으로 확장 메서드를 범위로 가져와서 호출합니다.

Imports ConsoleApplication2.StringExtensions

Module Module1

    Sub Main()
        Dim example As String = "Example string"
        example.Print()

        example = "Hello"
        example.PrintAndPunctuate(".")
        example.PrintAndPunctuate("!!!!")
    End Sub
End Module

이러한 확장 메서드 또는 유사한 확장 메서드를 실행할 수 있도록 하는 데 필요한 것은 범위에 있는 것입니다. 확장 메서드를 포함하는 모듈이 범위에 있으면 IntelliSense에 표시되며 일반 인스턴스 메서드인 것처럼 호출할 수 있습니다.

메서드가 호출될 때 첫 번째 매개 변수에 대한 인수가 전송되지 않습니다. 이전 메서드 정의의 매개 변수 aString 가 바인딩 example되어 해당 인스턴스 String 가 이를 호출합니다. 컴파일러는 첫 번째 매개 변수로 전송되는 인수로 사용합니다 example .

설정된 Nothing개체에 대해 확장 메서드를 호출하면 확장 메서드가 실행됩니다. 일반 인스턴스 메서드에는 적용되지 않습니다. 확장 메서드에서 명시적으로 확인할 Nothing 수 있습니다.

확장할 수 있는 형식

다음을 포함하여 Visual Basic 매개 변수 목록에 표시될 수 있는 대부분의 형식에서 확장 메서드를 정의할 수 있습니다.

  • 클래스(참조 형식)
  • 구조체(값 형식)
  • 인터페이스
  • 대리자
  • ByRef 및 ByVal 인수
  • 제네릭 메서드 매개 변수
  • 배열

첫 번째 매개 변수는 확장 메서드가 확장하는 데이터 형식을 지정하므로 필수이며 선택 사항이 될 수 없습니다. 따라서 Optional 매개 변수 및 ParamArray 매개 변수는 매개 변수 목록의 첫 번째 매개 변수가 될 수 없습니다.

확장 메서드는 늦은 바인딩에서 고려되지 않습니다. 다음 예제에서 문 anObject.PrintMe() 은 두 번째 PrintMe 확장 메서드 정의가 삭제되었는지와 동일한 예외인 예외를 발생 MissingMemberException 합니다.

Option Strict Off
Imports System.Runtime.CompilerServices

Module Module4

    Sub Main()
        Dim aString As String = "Initial value for aString"
        aString.PrintMe()

        Dim anObject As Object = "Initial value for anObject"
        ' The following statement causes a run-time error when Option
        ' Strict is off, and a compiler error when Option Strict is on.
        'anObject.PrintMe()
    End Sub

    <Extension()> 
    Public Sub PrintMe(ByVal str As String)
        Console.WriteLine(str)
    End Sub

    <Extension()> 
    Public Sub PrintMe(ByVal obj As Object)
        Console.WriteLine(obj)
    End Sub

End Module

모범 사례

확장 메서드는 기존 형식을 확장하는 편리하고 강력한 방법을 제공합니다. 그러나 성공적으로 사용하려면 몇 가지 사항을 고려해야 합니다. 이러한 고려 사항은 주로 클래스 라이브러리의 작성자에게 적용되지만 확장 메서드를 사용하는 모든 애플리케이션에 영향을 줄 수 있습니다.

일반적으로 소유하지 않는 형식에 추가하는 확장 메서드는 사용자가 제어하는 형식에 추가된 확장 메서드보다 더 취약합니다. 소유하지 않은 클래스에서 확장 메서드를 방해할 수 있는 여러 가지 상황이 발생할 수 있습니다.

  • 인수에서 매개 변수로의 축소 변환 없이 호출 문의 인수와 호환되는 서명이 있는 액세스 가능한 인스턴스 멤버가 있는 경우 인스턴스 메서드는 모든 확장 메서드에 대한 기본 설정에 사용됩니다. 따라서 적절한 인스턴스 메서드가 특정 시점에 클래스에 추가되면 사용하는 기존 확장 멤버에 액세스할 수 없게 될 수 있습니다.

  • 확장 메서드의 작성자는 다른 프로그래머가 원래 확장보다 우선할 수 있는 충돌하는 확장 메서드를 작성하는 것을 방지할 수 없습니다.

  • 확장 메서드를 자체 네임스페이스에 배치하여 견고성을 향상시킬 수 있습니다. 그런 다음 라이브러리의 소비자는 네임스페이스를 포함하거나 제외하거나 라이브러리의 나머지 부분과 별도로 네임스페이스 중에서 선택할 수 있습니다.

  • 특히 인터페이스 또는 클래스를 소유하지 않는 경우 클래스를 확장하는 것보다 인터페이스를 확장하는 것이 더 안전할 수 있습니다. 인터페이스의 변경은 인터페이스를 구현하는 모든 클래스에 영향을 줍니다. 따라서 작성자가 인터페이스에서 메서드를 추가하거나 변경할 가능성이 적을 수 있습니다. 그러나 클래스가 동일한 시그니처를 가진 확장 메서드가 있는 두 인터페이스를 구현하는 경우 두 확장 메서드는 모두 표시되지 않습니다.

  • 할 수 있는 가장 구체적인 형식을 확장합니다. 형식의 계층 구조에서 다른 많은 형식이 파생되는 형식을 선택하는 경우 인스턴스 메서드 또는 다른 확장 메서드를 도입할 수 있는 가능성이 있습니다.

확장 메서드, 인스턴스 메서드 및 속성

범위 내 인스턴스 메서드에 호출 문의 인수와 호환되는 서명이 있는 경우 인스턴스 메서드는 모든 확장 메서드에 대한 기본 설정으로 선택됩니다. 확장 메서드가 더 일치하는 경우에도 인스턴스 메서드의 우선 순위가 있습니다. 다음 예제 ExampleClass 에서는 하나의 형식 Integer매개 변수가 있는 명명된 ExampleMethod 인스턴스 메서드를 포함합니다. 확장 메서드 ExampleMethodExampleClass확장되고 형식의 매개 변수가 Long하나 있습니다.

Class ExampleClass
    ' Define an instance method named ExampleMethod.
    Public Sub ExampleMethod(ByVal m As Integer)
        Console.WriteLine("Instance method")
    End Sub
End Class

<Extension()> 
Sub ExampleMethod(ByVal ec As ExampleClass, 
                  ByVal n As Long)
    Console.WriteLine("Extension method")
End Sub

다음 코드에서 첫 번째 호출 ExampleMethod 은 확장 메서드 arg1Long 의 매개 변수와만 호환되므로 확장 메서드를 Long 호출합니다. 두 번째 호출 ExampleMethod 에는 인수arg2Integer 있으며 인스턴스 메서드를 호출합니다.

Sub Main()
    Dim example As New ExampleClass
    Dim arg1 As Long = 10
    Dim arg2 As Integer = 5

    ' The following statement calls the extension method.
    example.exampleMethod(arg1)
    ' The following statement calls the instance method.
    example.exampleMethod(arg2)
End Sub

이제 다음 두 메서드에서 매개 변수의 데이터 형식을 반대로 바꿉다.

Class ExampleClass
    ' Define an instance method named ExampleMethod.
    Public Sub ExampleMethod(ByVal m As Long)
        Console.WriteLine("Instance method")
    End Sub
End Class

<Extension()> 
Sub ExampleMethod(ByVal ec As ExampleClass, 
                  ByVal n As Integer)
    Console.WriteLine("Extension method")
End Sub

이번에는 인스턴스 메서드를 호출하는 Main 코드가 두 번 모두 호출됩니다. 이는 둘 다 arg1arg2 확장 변환 Long이 있고 인스턴스 메서드가 두 경우 모두 확장 메서드보다 우선하기 때문입니다.

Sub Main()
    Dim example As New ExampleClass
    Dim arg1 As Long = 10
    Dim arg2 As Integer = 5

    ' The following statement calls the instance method.
    example.ExampleMethod(arg1)
    ' The following statement calls the instance method.
    example.ExampleMethod(arg2)
End Sub

따라서 확장 메서드는 기존 인스턴스 메서드를 대체할 수 없습니다. 그러나 확장 메서드의 이름이 인스턴스 메서드와 같지만 서명이 충돌하지 않으면 두 메서드 모두에 액세스할 수 있습니다. 예를 들어 클래스 ExampleClass 에 인수를 사용하지 않는 메서드 ExampleMethod 가 포함된 경우 다음 코드와 같이 이름이 같지만 서명이 다른 확장 메서드가 허용됩니다.

Imports System.Runtime.CompilerServices

Module Module3

    Sub Main()
        Dim ex As New ExampleClass
        ' The following statement calls the extension method.
        ex.ExampleMethod("Extension method")
        ' The following statement calls the instance method.
        ex.ExampleMethod()
    End Sub

    Class ExampleClass
        ' Define an instance method named ExampleMethod.
        Public Sub ExampleMethod()
            Console.WriteLine("Instance method")
        End Sub
    End Class

    <Extension()> 
    Sub ExampleMethod(ByVal ec As ExampleClass, 
                  ByVal stringParameter As String)
        Console.WriteLine(stringParameter)
    End Sub

End Module

이 코드의 출력은 다음과 같습니다.

Extension method
Instance method

확장 메서드가 확장되는 클래스의 속성과 이름이 같으면 확장 메서드가 표시되지 않고 액세스할 수 없는 경우 속성이 더 간단해질 수 있습니다.

확장 메서드 우선 순위

동일한 서명이 있는 두 확장 메서드가 범위에 있고 액세스할 수 있는 경우 우선 순위가 더 높은 확장 메서드가 호출됩니다. 확장 메서드의 우선 순위는 메서드를 범위로 가져오는 데 사용되는 메커니즘을 기반으로 합니다. 다음 목록에서는 우선 순위 계층 구조를 가장 높은 계층에서 가장 낮은 계층으로 보여 있습니다.

  1. 현재 모듈 내에 정의된 확장 메서드입니다.

  2. 현재 네임스페이스 또는 부모 중 하나의 데이터 형식 내에 정의된 확장 메서드로, 자식 네임스페이스가 부모 네임스페이스보다 우선 순위가 높습니다.

  3. 현재 파일의 모든 형식 가져오기 내에 정의된 확장 메서드입니다.

  4. 현재 파일의 네임스페이스 가져오기 내에 정의된 확장 메서드입니다.

  5. 프로젝트 수준 형식 가져오기 내에 정의된 확장 메서드입니다.

  6. 프로젝트 수준 네임스페이스 가져오기 내에 정의된 확장 메서드입니다.

우선 순위가 모호성을 해결하지 못하는 경우 정규화된 이름을 사용하여 호출하는 메서드를 지정할 수 있습니다. 이전 예제의 Print 메서드가 명명 StringExtensions된 모듈에 정의된 경우 정규화된 이름이 StringExtensions.Print(example) 대신 example.Print()됩니다.

참조