System.Delegate와
이 문서에서는 대리자를 지원하는 .NET의 클래스와 이러한 클래스가 키워드에 delegate
매핑되는 방법을 설명합니다.
대리자 형식 정의
'delegate' 키워드로 시작해보겠습니다. 대리자를 사용할 때 주로 사용할 키워드이기 때문입니다. 키워드 delegate
를 사용할 때 컴파일러가 생성하는 코드는 Delegate 및 MulticastDelegate 클래스의 멤버를 호출하는 메서드 호출에 매핑됩니다.
메서드 시그니처 정의와 유사한 구문을 사용하여 대리자 형식을 정의합니다. 정의에 delegate
키워드를 추가하기만 하면 됩니다.
List.Sort() 메서드를 예제로 계속 사용하겠습니다. 첫 번째 단계는 비교 대리자의 형식을 만드는 것입니다.
// From the .NET Core library
// Define the delegate type:
public delegate int Comparison<in T>(T left, T right);
컴파일러는 사용된 서명과 일치하는 클래스 System.Delegate
를 생성합니다(이 경우 정수를 반환하고 두 개의 인수가 있는 메서드). 해당 대리자의 형식은 Comparison
입니다.
Comparison
대리자 형식은 제네릭 형식입니다. 자세한 내용은 제네릭 클래스 및 메서드를 참조하세요.
구문은 변수를 선언하는 것처럼 나타날 수 있지만 실제로 는 형식을 선언합니다. 클래스 내부, 네임스페이스 내부 또는 전역 네임스페이스에서 대리자 형식을 정의할 수 있습니다.
비고
전역 네임스페이스에서 직접 대리자 형식(또는 기타 형식)을 선언하는 것은 권장되지 않습니다.
또한 컴파일러는 이 클래스의 클라이언트가 인스턴스의 호출 목록에서 메서드를 추가하고 제거할 수 있도록 이 새 형식에 대한 추가 및 제거 처리기를 생성합니다. 컴파일러는 추가 또는 제거되는 메서드의 서명이 메서드를 선언할 때 사용되는 서명과 일치하게 합니다.
대리자의 인스턴스 선언
대리자를 정의한 후 해당 형식의 인스턴스를 만들 수 있습니다. C#의 모든 변수와 마찬가지로 네임스페이스 또는 전역 네임스페이스에서 직접 대리자 인스턴스를 선언할 수 없습니다.
// inside a class definition:
// Declare an instance of that type:
public Comparison<T> comparator;
변수의 형식은 앞에서 정의한 대리자 형식입니다 Comparison<T>
. 변수의 이름은 .입니다 comparator
.
위의 코드 조각은 클래스 내에서 멤버 변수를 선언했습니다. 지역 변수인 대리자 변수 또는 메서드에 대한 인수를 선언할 수도 있습니다.
대리자 호출
해당 대리자를 호출하여 대리자 호출 목록에 있는 메서드를 호출합니다. 메서드 내에서 Sort()
코드는 비교 메서드를 호출하여 개체를 배치할 순서를 결정합니다.
int result = comparator(left, right);
위의 줄에서 코드는 대리자에 연결된 메서드를 호출합니다 . 변수를 메서드 이름으로 처리하고 일반 메서드 호출 구문을 사용하여 호출합니다.
해당 코드 줄은 위험한 가정을 전제로 합니다. 대상이 대리자에 추가되었다는 보장은 없습니다. 대상이 연결되지 않은 경우 위의 줄로 인해 NullReferenceException
예외가 발생합니다. 이 문제를 해결하는 데 사용되는 관용구는 간단한 null 검사보다 더 복잡하며 이 시리즈의 뒷부분에서 다룹니다.
호출 대상 할당, 추가 및 제거
대리자 형식을 정의하는 방법과 대리자 인스턴스를 선언하고 호출하는 방법입니다.
이 메서드를 사용 List.Sort()
하려는 개발자는 시그니처가 대리자 형식 정의와 일치하는 메서드를 정의하고 정렬 메서드에서 사용하는 대리자에게 할당해야 합니다. 이 할당은 해당 대리자 개체의 호출 목록에 메서드를 추가합니다.
문자열 목록을 길이별로 정렬하려 했다고 가정해 보겠습니다. 비교 함수는 다음과 같을 수 있습니다.
private static int CompareLength(string left, string right) =>
left.Length.CompareTo(right.Length);
메서드는 private 메서드로 선언됩니다. 괜찮습니다. 이 메서드가 공용 인터페이스의 일부가 되는 것을 원하지 않을 수 있습니다. 대리자를 연결할 때 비교 메서드로 계속 사용할 수 있습니다. 호출 코드는 이 메서드를 대리자 개체의 대상 목록에 연결하고 해당 대리자를 통해 액세스할 수 있습니다.
메서드에 List.Sort()
메서드를 전달하여 해당 관계를 만듭니다.
phrases.Sort(CompareLength);
메서드 이름은 괄호 없이 사용됩니다. 메서드를 인수로 사용하면 컴파일러가 메서드 참조를 대리자 호출 대상으로 사용할 수 있는 참조로 변환하고 해당 메서드를 호출 대상으로 연결하도록 지시합니다.
형식 Comparison<string>
의 변수를 선언하고 할당을 수행하여 명시적일 수도 있습니다.
Comparison<string> comparer = CompareLength;
phrases.Sort(comparer);
대리자 대상으로 사용되는 메서드가 작은 메서드인 경우 람다 식 구문을 사용하여 할당을 수행하는 것이 일반적입니다.
Comparison<string> comparer = (left, right) => left.Length.CompareTo(right.Length);
phrases.Sort(comparer);
대리자 대상에 람다 식을 사용하는 방법은 이후 섹션에서 자세히 설명합니다.
Sort() 예제에서는 일반적으로 대리자에게 단일 대상 메서드를 할당합니다. 그러나 대리자 개체는 대리자 개체에 연결된 여러 대상 메서드가 있는 호출 목록을 지원합니다.
Delegate 및 MulticastDelegate 클래스
위에서 설명한 언어 지원은 일반적으로 대리인과 함께 작업하는 데 필요한 기능과 지원을 제공합니다. 이러한 기능은 .NET Core 프레임워크의 두 클래스인 Delegate 및 MulticastDelegate 위에 구축됩니다.
System.Delegate
클래스와 해당 단일 직접 서브클래스는 System.MulticastDelegate
대리자를 만들고, 메서드를 대리자 대상으로 등록하고, 대리자 대상으로 등록된 모든 메서드를 호출하기 위한 프레임워크 지원을 제공합니다.
흥미롭게도 System.Delegate
클래스와 System.MulticastDelegate
클래스는 대리자 형식이 아닙니다. 모든 특정 대리자 형식에 대한 기초를 제공합니다. 같은 언어 설계 과정으로 인해 Delegate
또는 MulticastDelegate
에서 파생되는 클래스를 선언할 수 없습니다. C# 언어 규칙은 이를 금지합니다.
대신 C# 컴파일러는 C# 언어 키워드를 사용하여 대리자 형식을 선언할 때 파생된 MulticastDelegate
클래스의 인스턴스를 만듭니다.
이 디자인은 C# 및 .NET의 첫 번째 릴리스에 해당합니다. 디자인 팀의 한 가지 목표는 대리자를 사용할 때 언어가 형식 안전을 적용하도록 하는 것이었습니다. 즉, 대리자가 올바른 형식과 인수 수를 사용하여 호출되도록 했습니다. 또한 모든 반환 형식이 컴파일 시간에 올바르게 표시되었습니다. 대리자는 제네릭 이전의 1.0 .NET 릴리스의 일부였습니다.
이 형식 보안을 적용하는 가장 좋은 방법은 컴파일러가 사용 중인 메서드 시그니처를 나타내는 구체적인 대리자 클래스를 만드는 것이었습니다.
파생 클래스를 직접 만들 수는 없지만 이러한 클래스에 정의된 메서드를 사용합니다. 대리자를 사용할 때 사용할 가장 일반적인 방법을 살펴보겠습니다.
기억해야 할 가장 중요한 첫 번째 사실은 당신이 함께 작업하는 모든 대리자가 MulticastDelegate
에서 파생된다는 것입니다. 멀티캐스트 대리자는 대리자를 통해 호출할 때 둘 이상의 메서드 대상을 호출할 수 있음을 의미합니다. 원래 디자인에서는 하나의 대상 메서드만 연결 및 호출할 수 있는 대리자와 여러 대상 메서드를 연결하고 호출할 수 있는 대리자를 구분하는 것을 고려했습니다. 그 구별은 원래 생각보다 실제로 덜 유용 한 것으로 판명. 서로 다른 두 클래스는 이미 만들어졌으며, 초기 공개 릴리스 이후 프레임워크에 있었습니다.
대리자와 함께 가장 자주 사용할 메서드는 다음과 Invoke()
BeginInvoke()
/ 같습니다.EndInvoke()
Invoke()
는 특정 대리자 인스턴스에 연결된 모든 메서드를 호출합니다. 위에서 본 것처럼 일반적으로 대리자 변수에서 메서드 호출 구문을 사용하여 대리자를 호출합니다.
이 시리즈의 뒷부분에서 볼 수 있듯이 이러한 메서드와 직접 작동하는 패턴이 있습니다.
이제 언어 구문과 대리자를 지원하는 클래스를 살펴보았으므로 강력한 형식의 대리자가 사용, 생성 및 호출되는 방법을 살펴보겠습니다.
.NET