확장 메서드를 사용하면 개발자가 새 파생 형식을 만들지 않고 이미 정의된 데이터 형식에 사용자 지정 기능을 추가할 수 있습니다. 확장 메서드를 사용하면 기존 형식의 인스턴스 메서드인 것처럼 호출할 수 있는 메서드를 작성할 수 있습니다.
비고
확장 메서드는 Sub
프로시저 또는 Function
프로시저일 수 있습니다. 확장 속성, 필드 또는 이벤트를 정의할 수 없습니다. 모든 확장 메서드는 네임스페이스의 확장 특성 <Extension>
System.Runtime.CompilerServices 으로 표시되어야 하며 모듈에서 정의해야 합니다. 확장 메서드가 모듈 외부에서 정의되면 Visual Basic 컴파일러는 "확장 메서드는 모듈에서만 정의할 수 있습니다" 라는 오류 BC36551 생성합니다.
확장 메서드 정의의 첫 번째 매개 변수는 메서드가 확장하는 데이터 형식을 지정합니다. 메서드가 실행되면 첫 번째 매개 변수는 메서드를 호출하는 데이터 형식의 인스턴스에 바인딩됩니다.
이 속성은 Extension
, Visual Basic Module
, Sub
, 또는 Function
에만 적용할 수 있습니다.
Class
또는 Structure
에 적용하면 Visual Basic 컴파일러는 오류 BC36550를 생성합니다. "'Extension' 특성은 'Module', 'Sub' 또는 'Function' 선언에만 적용할 수 있습니다."
예시
다음 예제에서는 데이터 형식에 대한 Print
String 확장을 정의합니다. 메서드는 문자열을 표시하는 데 사용합니다 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(".")
다음 예제에서는 Print
정의 및 PrintAndPunctuate
호출합니다.
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()
은 두 번째 MissingMemberException 확장 메서드 정의가 삭제된 경우 발생하는 것과 동일한 PrintMe
예외를 발생시킵니다.
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
에서는 하나의 ExampleMethod
매개 변수 형식이 있는 명명된 Integer
인스턴스 메서드를 포함합니다. 확장 메서드 ExampleMethod
는 ExampleClass
확장되며 형식의 매개 변수가 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
의 첫 번째 호출은 arg1
이 Long
이며 확장 메서드의 Long
매개 변수와만 호환되기 때문에 확장 메서드를 호출합니다. 두 번째 ExampleMethod
호출에는 Integer
인자arg2
가 있으며, 인스턴스 메서드를 호출합니다.
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
인스턴스 메서드를 두 번 호출합니다. 이는 arg1
와 arg2
모두 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
확장 메서드가 확장되는 클래스의 속성과 이름이 같으면 확장 메서드가 표시되지 않고 액세스할 수 없는 경우 속성을 사용하는 것이 더 간단합니다.
확장 메서드 우선 순위
서명이 동일한 두 확장 메서드가 범위에 있고 액세스할 수 있는 경우 우선 순위가 더 높은 메서드가 호출됩니다. 확장 메서드의 우선 순위는 메서드를 범위로 가져오는 데 사용되는 메커니즘을 기반으로 합니다. 다음 목록에서는 우선 순위 계층을 가장 높은 계층에서 가장 낮은 계층으로 보여 있습니다.
현재 모듈 내에 정의된 확장 메서드입니다.
부모 네임스페이스보다 우선 순위가 높은 자식 네임스페이스가 있는 현재 네임스페이스 또는 부모 중 하나의 데이터 형식 내에 정의된 확장 메서드입니다.
현재 파일의 모든 형식 가져오기 내에 정의된 확장 메서드입니다.
현재 파일의 네임스페이스 가져오기 내에 정의된 확장 메서드입니다.
프로젝트 수준 형식 가져오기 내에 정의된 확장 메서드입니다.
프로젝트 수준 네임스페이스 가져오기 내에 정의된 확장 메서드입니다.
우선 순위가 모호성을 해결하지 못하는 경우 정규화된 이름을 사용하여 호출하는 메서드를 지정할 수 있습니다. 이전 예제의 Print
메서드가 StringExtensions
라는 이름의 모듈에 정의된 경우, 정규화된 이름은 StringExtensions.Print(example)
이며, example.Print()
가 아닙니다.
참고하십시오
.NET