확장 방식
Extension method![]() |
객체 지향 컴퓨터 프로그래밍에서 확장 메서드는 원래 객체가 컴파일된 후 객체에 추가된 메서드입니다.수정된 개체는 클래스, 프로토타입 또는 유형인 경우가 많습니다.확장 메서드는 일부 객체 지향 프로그래밍 언어의 기능입니다.확장 메서드를 호출하는 것과 유형 [1]정의에서 선언된 메서드를 호출하는 것에는 구문적인 차이가 없습니다.
그러나 모든 언어가 확장 방식을 동일하게 안전한 방식으로 구현하는 것은 아닙니다.예를 들어 C#, Java, Kotlin 등의 언어는 확장 클래스를 변경하지 않습니다.이는 확장 클래스를 변경하면 클래스 계층이 깨지고 가상 메서드 디스패치가 방해될 수 있기 때문입니다.그렇기 때문에 이들 언어는 확장 방식을 정적으로 엄격하게 구현하고 정적 디스패치를 사용하여 호출합니다.
프로그래밍 언어 지원
확장 방식은 C#, Java via Multiplex 또는 Lombok, Gosu, JavaScript, Oxygene, Ruby, Smalltalk, Kotlin, Dart, Visual Basic 등 다양한 언어의 기능입니다.NET 및 Xojo.Python과 같은 동적 언어에서는 특별한 구문(gevent와 같은 라이브러리에서 사용되는 "monkey-patching"으로 알려진 접근법) 없이 클래스를 확장할 수 있기 때문에 확장 메서드의 개념이 필요하지 않습니다.
VB.NET 및 옥시진에서는 다음 물질의 존재에 의해 인식된다.extension
키워드 또는 속성.Xojo에서는 "Extends
키워드는 글로벌 방식에서 사용됩니다.
C#에서는 스태틱클래스에서 스태틱메서드로 구현되며 첫 번째 인수는 확장 클래스이고 앞에 "가 붙습니다.this
키워드.
Java에서는 프로젝트의 클래스 경로에 추가하는 jar 파일인 매니폴드를 통해 확장 메서드를 추가합니다.C#과 마찬가지로 Java 확장 메서드는 @Extension 클래스에서 static으로 선언되며 첫 번째 인수는 확장 클래스와 동일한 유형으로 주석 지정됩니다.@This
.
Smalltalk에서는 메서드 생성 메시지를 전송함으로써 언제든지 모든 클래스에 메서드를 추가할 수 있습니다(예:methodsFor:
)을 클릭합니다.Smalltalk 메서드카테고리는 일반적으로 확장자를 제공하는 패키지의 이름을 따 별표로 둘러싸여 있습니다.예를 들어 Etoys 응용 프로그램코드가 코어 라이브러리의 클래스를 확장하면 추가된 메서드가*etoys*
카테고리.
Smalltalk와 같이 Ruby에서는 특별한 확장 언어 기능이 없습니다.Ruby는 언제든지 클래스를 다시 열 수 있기 때문입니다.class
키워드(이 예에서는 새로운 메서드를 추가합니다).Ruby 커뮤니티에서는 확장 방법을 원숭이 패치의 일종이라고 설명하기도 합니다.오브젝트에 세이프/로컬 확장을 추가하기 위한 새로운 기능인 미세 조정도 있지만 사용 빈도는 낮은 것으로 알려져 있습니다.
Swift에서는extension
키워드를 지정하면 기존 클래스에 메서드, 컨스트럭터 및 필드를 추가할 수 있는 클래스와 같은 구조(기존 [2]클래스에 새로운 인터페이스/클래스를 실장하는 기능 포함)가 표시됩니다.
기능을 활성화하는 확장 방식
다른 사람이 작성한 코드를 아래 설명과 같이 확장할 수 있는 확장 방식 외에 확장 방식은 그 자체로 유용한 패턴을 가능하게 합니다.확장 방식이 도입된 주된 이유는 Language Integrated Query(LINQ; 언어 통합 쿼리)입니다.확장 메서드에 대한 컴파일러 지원을 통해 LINQ를 새로운 코드와 동일하게 오래된 코드와 긴밀하게 통합할 수 있을 뿐만 아니라 현재는 프라이머리 Microsoft에 고유한 쿼리 구문을 지원할 수 있습니다.NET 언어
콘솔.기입선(신규[] { 수학.PI, 수학.E }.어디에(d => d > 3).선택한다.(d => 수학.신(d / 2)).합()); // 출력: // 1
일반적인 동작의 일원화
단, 확장 메서드는 상속이나 가상 메서드 호출의 오버헤드 없이 재사용할 수 있는 방법으로 기능을 한 번 구현하거나 인터페이스의 구현자가 사소한 기능 또는 매우 복잡한 기능을 구현하도록 요구할 수 있습니다.
특히 유용한 시나리오는 기능이 구체적인 구현이 없는 인터페이스 상에서 동작하거나 클래스 라이브러리 작성자에 의해 유용한 구현이 제공되지 않는 경우입니다. 예를 들어 개발자에게 플러그인 아키텍처 또는 유사한 기능을 제공하는 라이브러리에서 흔히 볼 수 있는 경우입니다.
다음 코드를 고려하여 클래스 라이브러리에 포함된 유일한 코드라고 가정합니다.그러나 ILogger 인터페이스의 모든 구현자는 MyCoolLogger 문을 사용하는 것만으로 포맷된 문자열을 쓸 수 있습니다.한 번 구현할 필요도 없고 ILogger 구현 시 클래스 라이브러리를 서브클래스로 할 필요도 없습니다.
네임스페이스 My Cool Logger; 일반의 인터페이스 로거 { 무효 기입하다(스트링 본문); } 일반의 정적인 학급 로거 확장 { 일반의 정적인 무효 기입하다(이것. 로거 로거, 스트링 포맷, 파라미터 물건[] args) { 한다면 (로거 != 무효) 로거.기입하다(스트링.포맷(포맷, args)); } }
- 다음과 같이 사용합니다.
변화하다 로거 = 신규 My Logger 구현(); 로거.기입하다("{0}: {1}", "sais", 엄마 엄마 엄마...); 로거.기입하다("{0}: {1}", "sais", "엄마, 엄마..."); 로거.기입하다("{0}: {1}", "sais", '마마마마마마마마마마마마'); 로거.기입하다("{0}: {1}", "sais", "엄마, 엄마..."); 로거.기입하다("{0}: {1}", "sais", "엘리자베스 리지 리즈..."); 로거.기입하다("{0}: {1}", '사이스', "뭐야?!!"); 로거.기입하다("{0}: {1}", "sais", "안녕하세요");
느슨한 커플링 개선
확장 메서드를 사용하면 클래스 라이브러리의 사용자가 해당 라이브러리에서 가져온 유형의 인수, 변수 또는 기타 항목을 선언하지 않도록 할 수 있습니다.클래스 라이브러리에서 사용되는 유형의 구축 및 변환은 확장 방식으로 구현될 수 있습니다.변환과 팩토리를 신중하게 구현한 후 클래스 라이브러리 간에 전환하는 것은 컴파일러가 바인드할 확장 메서드를 사용할 수 있도록 하는 using 스테이트먼트를 변경하는 것만큼이나 간단합니다.
유창한 응용 프로그램 프로그래머 인터페이스
확장 방식은 이른바 fluent 인터페이스 구현에 특별한 용도가 있습니다.예를 들어 Microsoft의 Entity Framework 구성 API를 사용하면 일반 영어와 유사한 코드를 실용적으로 작성할 수 있습니다.
확장 메서드 없이도 이것이 가능하다고 주장할 수 있지만, 실제로는 확장 메서드가 클래스 계층에 더 적은 제약이 주어져 원하는 대로 작동하고 읽기 때문에 더 나은 경험을 제공한다는 것을 알게 될 것이다.
다음 예제에서는 엔티티 프레임워크를 사용하여 데이터베이스 테이블목록에 저장되도록 TodoList 클래스를 설정하고 프라이머리 키와 외부 키를 정의합니다.코드는 다음과 같이 이해해야 합니다. "ToDoList에는 ToDoList 키가 있습니다.ID, 엔티티 세트명은 Lists이며 각각 필수 TodoList를 가진 많은 TodoItem이 있습니다."
일반의 학급 ToDoItem Context(작업 내용) : DbContext { 일반의 DbSet< >할 일> 작업 항목 { 얻다; 세트; } 일반의 DbSet< >할 일 목록> 작업 목록 { 얻다; 세트; } 보호되고 있다 덮어쓰다 무효 OnModel 작성(Db Model Builder 모델빌더) { 기초.OnModel 작성(모델빌더); 모델빌더.독립체< >할 일 목록>() .HasKey 키(e => e.ToDoListId) .HasEntitySetName(HasEntitySetName)("목록) .하스매니(e => e.할 일) .필수 포함(e => e.할 일 목록); } }
생산성
예를 들어 IEnumerable과 그 단순성에 주목해 주십시오.단, 방법은 1개뿐이지만 LINQ의 기초가 됩니다.Microsoft 에는, 이 인터페이스의 실장이 다수 있습니다.NET. 그럼에도 불구하고, 시스템에 정의되어 있는 일련의 방법을 모두 구현하도록 이들 각 구현에 요구하는 것은 분명 부담스러운 일이었을 것이다.IEnumerables에서 동작하는 Linq 네임스페이스(Microsoft가 모든 소스 코드를 가지고 있는 경우).게다가 이 경우, Microsoft 이외의 모든 사람이 IENumerable을 사용해 이러한 모든 방법을 실장할 필요가 있습니다.이러한 일반적인 인터페이스가 널리 사용되고 있는 것을 보면, 매우 생산성이 떨어집니다.대신 이 인터페이스의 1가지 방식을 구현함으로써 LINQ를 거의 즉시 사용할 수 있습니다.특히 대부분의 경우 IENumerable의 GetEnumerator 메서드는 개인 컬렉션, 목록 또는 어레이의 GetEnumerator 구현에 위임되어 있습니다.
일반의 학급 은행 계좌 : IENumerable< >십진수> { 사적인 목록.< >튜플< >날짜 시간, 십진수>> 크레디트; // 모두 음수라고 가정함 사적인 목록.< >튜플< >날짜 시간, 십진수>> 대변; // 모든 것이 긍정적이라고 가정함 일반의 IENumerator< >십진수> Get Enumerator() { 변화하다 질문하다 = 부터 직류 에 대변.유니언(크레디트) 오더바이 직류.항목 1 /* 날짜 */ 선택한다. 직류.아이템2; /* 금액 */ 앞지르다 (변화하다 양 에 질문하다) 산출하다 돌아가다 양; } } // BankAccount 인스턴스 ba와 사용 중인 시스템.현재 파일 맨 위에 있는 LINQ, // 이제 ba라고 쓸 수 있습니다.계정 잔액을 가져오려면 Sum()을 클릭합니다.Reverse()는 가장 최근의 트랜잭션을 먼저 표시합니다. // ba.Average() - 연산자를 쓰지 않고 트랜잭션당 평균 금액을 가져옵니다.
성능
즉, 확장 방법에 의해 제공되는 기능의 구현을 추가하여 성능을 향상시키거나 컴파일러에 (시스템 내) 어레이 전용 IEnumerable 구현을 제공하는 등 다른 인터페이스 구현에 대처할 수 있습니다.SZArrayHelper는 어레이 타입의 참조에 대한 확장 메서드 호출에 대해 자동으로 선택됩니다.인수가 IEnumerable 인터페이스(이 IEnumerable 값) 인스턴스에서 동작하는 것과 같은 이름의 확장 메서드보다 구체적이기 때문입니다.
공통 기본 클래스의 필요성 완화
범용 클래스의 경우 확장 메서드를 사용하면 일반적인 유형의 모든 인스턴스화에 사용할 수 있는 동작을 구현할 수 있습니다.이 동작은 공통 기본 클래스에서 파생되지 않아도 됩니다.또한 타입 파라미터를 특정 상속 브랜치로 제한하지 않습니다.이 인수가 유지되고 있는 상황에서는 공유 기능을 구현하기 위해서만 일반적이지 않은 기본 클래스가 필요하기 때문에 이것은 큰 이점이 됩니다.이 경우 사용되는 유형이 유형 인수 중 하나일 때마다 일반 하위 클래스가 boxing 및/또는 캐스트를 수행해야 합니다.
보존적 사용
재사용 및 적절한 객체 지향 설계를 실현하기 위한 다른 방법보다 확장 방법을 선호한다는 점에 유의해야 합니다.확장 메서드는 Visual Studio의 IntelliSense와 같은 코드 편집기의 자동 완료 기능을 '커터링'할 수 있으므로 개발자가 선택적으로 가져올 수 있도록 자체 네임스페이스에 있거나 메서드가 IntelliSense에 표시될 수 있을 만큼 구체적인 유형으로 정의되어야 합니다.t와 위의 내용을 고려할 때 개발자가 예상하는 경우 찾기가 어려울 수 있지만 IntelliSense에서 놓칠 수 있습니다.왜냐하면 개발자는 메서드를 정의하는 클래스나 네임스페이스와 연관짓지 않았을 수 있기 때문입니다.그 대신 확장한 타입과 네임스팩을 사용할 수 있기 때문입니다.그런 타입이 살고 있다.
문제
프로그래밍에서는 새로운 메서드를 추가하는 등 기존 클래스에 기능을 추가해야 하는 상황이 발생합니다.보통 프로그래머는 기존 클래스의 소스 코드를 수정하지만, 이것은 프로그래머가 이러한 새로운 변경으로 모든 바이너리를 재컴파일하도록 강요하고, 프로그래머가 서드파티 어셈블리의 클래스를 사용할 때 항상 가능한 것은 아니다.이는 일반적으로 다음 세 가지 방법 중 하나로 해결되며, 모두 다소 제한적이고 의도적이지 않습니다[citation needed].
현재의 C# 솔루션
첫 번째 선택은 원칙적으로 더 쉽지만 안타깝게도 많은 계층이 특정 구성원의 상속을 제한하거나 완전히 금지한다는 사실에 의해 제한된다.여기에는 sealled 클래스 및 int, float, string 등 C# 내의 다양한 원시 데이터 유형이 포함됩니다.반면에 두 번째 옵션은 이러한 제약을 공유하지는 않지만, 해당 세분류의 방법을 직접 사용하는 대신 별도의 세분류에 대한 참조를 요구하기 때문에 덜 직관적일 수 있다.
예를 들어 문자열 클래스를 새로운 역방향 메서드로 확장해야 합니다.이 메서드의 반환값은 역순으로 문자를 포함하는 문자열입니다.문자열 클래스는 밀폐형이기 때문에 메서드는 보통 다음과 같은 방법으로 새로운 유틸리티 클래스에 추가됩니다.
스트링 x = "일부 문자열 값"; 스트링 y = 효용..리버스(x);
그러나 유틸리티 방법과 클래스의 라이브러리가 증가함에 따라, 특히 신규 사용자에게는 이 방법을 탐색하는 것이 점점 더 어려워질 수 있다.또한 대부분의 문자열 메서드와 달리 이 위치는 문자열 클래스의 멤버가 아니라 완전히 다른 클래스에 속하기 때문에 직관성이 떨어집니다.따라서 보다 나은 구문은 다음과 같습니다.
스트링 x = "일부 문자열 값"; 스트링 y = x.리버스();
현재 VBNET 솔루션
대부분의 경우, VB입니다.NET 솔루션은 위의 C# 솔루션과 유사합니다.하지만 VB.NET 에는, 멤버를 참조에 의해서 내선 번호에 건네줄 수 있는 독자적인 이점이 있습니다(C# allowed by value).다음을 허용합니다.
어둡다 x ~하듯이 스트링 = "일부 문자열 값" x.리버스()
Visual Basic에서는 소스 개체를 참조로 전달할 수 있으므로 다른 변수를 생성하지 않고도 소스 개체를 직접 변경할 수 있습니다.또한 기존 수업 방식과 일관되게 작동하기 때문에 더욱 직관적입니다.
확장 방식
다만, C# 3.0 의 확장 메서드의 새로운 언어 기능에 의해서, 후자의 코드가 가능하게 됩니다.이 방법에는 다음과 같은 스태틱클래스와 스태틱 방식이 필요합니다.
일반의 정적인 학급 효용. { 일반의 정적인 스트링 리버스(이것. 스트링 입력) { 차[] 문자 = 입력.ToCharArray(ToCharArray)(); 어레이.리버스(문자); 돌아가다 신규 스트링(문자); } }
정의에서 첫 번째 인수 앞에 있는 수식자 'this'는 확장 메서드임을 나타냅니다(이 경우 'string' 유형).콜에서 첫 번째 인수는 이미 콜 오브젝트(도트 앞의 오브젝트)로 알려져 있기 때문에 pass in이 되지 않습니다.
콜 확장 방식과 콜링 스태틱도우미 방식의 주요 차이점은 스태틱 방식이 프리픽스 표기법으로 호출되는 반면 내선 방식은 infix 표기법으로 호출된다는 것입니다.후자는 한 작업의 결과가 다른 작업에 사용될 때 더 읽기 쉬운 코드로 이어집니다.
- 정적 방식 사용
도우미 클래스.조작 2(도우미 클래스.조작 1(x, arg1), arg2)
- 확장 방식 사용
x.조작 1(arg1).조작 2(arg2)
확장 메서드 및 인스턴스 메서드에서 이름 충돌
C# 3.0에서는 동일한 시그니처를 가진 인스턴스 메서드와 확장 메서드가 클래스에 모두 존재할 수 있습니다.이러한 시나리오에서는 확장 방식보다 인스턴스 방식이 선호됩니다.컴파일러도 Microsoft Visual Studio IDE도 명명 충돌에 대해 경고하지 않습니다.이 C# 클래스를 고려합니다.GetAlphabet()
method는 이 클래스의 인스턴스에서 호출됩니다.
학급 알파벳 메이커 { 일반의 무효 알파벳 가져오기() { //이 메서드가 구현되면 콘솔.기입선("실패"); //실장 그림자가 표시됩니다. } //ExtensionMethods 클래스에서 사용합니다. } 정적인 학급 확장 방법 { 일반의 정적인 무효 알파벳 가져오기(이것. 알파벳 메이커 am) { //이것만 호출됩니다. 콘솔.기입선('ABC"); //인스턴스가 없는 경우 } //동일한 시그니처를 사용하여 서명합니다. }
호출 결과GetAlphabet()
의 예로서AlphabetMaker
확장 방식만 존재하는 경우:
ABC
인스턴스 메서드와 확장 메서드가 모두 존재하는 경우 결과:
abc
「 」를 참조해 주세요.
레퍼런스
- ^ "Extension Methods". Microsoft. Retrieved 2008-11-23.
- ^ "Extensions — The Swift Programming Language (Swift 5.7)". docs.swift.org. Retrieved 2022-06-12.