다음을 통해 공유


대리자 사용(C# 프로그래밍 가이드)

대리자는 메서드를 안전하게 캡슐화하는 형식으로, C 및 C++의 함수 포인터와 유사합니다. C의 함수 포인터와 달리 대리자는 개체 지향적이고 형식 안전성을 제공하며 보안상 안전합니다. 대리자의 형식은 대리자의 이름으로 정의됩니다. 다음 예제에서는 문자열을 인수로 사용하고 void를 반환하는 메서드를 캡슐화할 수 있는 Del이라는 대리자를 선언합니다.

public delegate void Del(string message);

대리자 개체는 일반적으로 대리자가 래핑할 메서드의 이름을 제공하거나 무명 메서드를 사용하는 방법으로 만들어집니다. 대리자가 인스턴스화되면 대리자에 대한 메서드 호출이 해당 메서드로 전달됩니다. 호출자가 대리자에 전달한 매개 변수가 메서드에 전달되고, 메서드에 반환 값이 있으면 대리자가 해당 값을 호출자에게 반환합니다. 이 과정을 대리자 호출이라고 합니다. 사용자는 인스턴스화된 대리자를 래핑된 메서드 자체인 것처럼 호출할 수 있습니다. 예를 들면 다음과 같습니다.

// Create a method for a delegate.
public static void DelegateMethod(string message)
{
    System.Console.WriteLine(message);
}
// Instantiate the delegate.
Del handler = DelegateMethod;

// Call the delegate.
handler("Hello World");

대리자 형식은 .NET Framework의 Delegate 클래스에서 파생됩니다. 대리자 형식은 sealed(파생될 수 없음)로서, Delegate에서 사용자 지정 클래스를 파생할 수 없습니다. 인스턴스화된 대리자는 개체이므로 매개 변수로 전달하거나 속성에 할당할 수 있습니다. 따라서 메서드에서는 대리자를 매개 변수로 받은 다음 이후에 대리자를 호출할 수 있습니다. 이를 비동기 콜백이라고 하며, 시간이 걸리는 프로세스가 완료되었을 때 호출자에게 알리는 일반적인 방법입니다. 대리자가 이러한 방식으로 사용되는 경우 대리자를 사용하는 코드에서는 사용되고 있는 메서드의 구현에 대한 정보가 필요하지 않습니다. 이 기능은 캡슐화 인터페이스에서 제공하는 기능과 유사합니다. 자세한 내용은 인터페이스 대신 대리자를 사용해야 하는 경우를 참조하십시오.

콜백은 사용자 지정 비교 메서드를 정의한 다음 이 대리자를 정렬 메서드에 전달하는 데 일반적으로 사용되기도 합니다. 이렇게 하면 호출자의 코드가 정렬 알고리즘의 일부가 됩니다. 다음 예제 메서드에서는 Del 형식을 매개 변수로 사용합니다.

public void MethodWithCallback(int param1, int param2, Del callback)
{
    callback("The number is: " + (param1 + param2).ToString());
}

그런 다음 위에서 생성된 대리자를 다음 메서드에 전달할 수 있습니다.

MethodWithCallback(1, 2, handler);

그러면 콘솔에 다음과 같이 출력됩니다.

The number is: 3

대리자를 추상화로 사용하므로 MethodWithCallback에서는 콘솔을 직접 호출할 필요가 없으며, 이 메서드를 디자인할 때 콘솔을 고려할 필요가 없습니다. MethodWithCallback에서는 문자열을 준비하여 다른 메서드에 전달할 뿐입니다. 이러한 방식은 대리자가 지정된 메서드에서 사용할 수 있는 매개 변수의 개수에 제한이 없으므로 특히 강력합니다.

대리자가 인스턴스 메서드를 래핑하도록 만들어진 경우에는 대리자에서 인스턴스와 메서드를 모두 참조합니다. 대리자에는 래핑되는 메서드 외에는 인스턴스 형식에 대한 정보가 없으므로, 개체에 대리자 시그니처와 일치하는 메서드가 있기만 하면 대리자에서 이 개체의 형식을 참조할 수 있습니다. 대리자가 정적 메서드를 래핑하도록 만들어진 경우에는 대리자에서 메서드만 참조합니다. 다음 선언을 참조하십시오.

public class MethodClass
{
    public void Method1(string message) { }
    public void Method2(string message) { }
}

앞에 나왔던 정적 DelegateMethod를 포함하여 이제 Del 인스턴스에서 래핑할 수 있는 세 개의 메서드가 있습니다.

대리자에서는 자신이 호출될 때 하나 이상의 메서드를 호출할 수 있습니다. 이를 멀티캐스팅이라고 합니다. 대리자의 메서드 목록(호출 목록)에 메서드를 추가하려면 더하기 연산자('+') 또는 더하기 할당 연산자('+=')를 사용하여 대리자 두 개를 더하기만 하면 됩니다. 예를 들면 다음과 같습니다.

MethodClass obj = new MethodClass();
Del d1 = obj.Method1;
Del d2 = obj.Method2;
Del d3 = DelegateMethod;

//Both types of assignment are valid.
Del allMethodsDelegate = d1 + d2;
allMethodsDelegate += d3;

이 시점에서 allMethodsDelegate의 호출 목록에는 Method1, Method2 및 DelegateMethod라는 세 메서드가 포함되어 있습니다. 원래 대리자 세 개, 즉 d1, d2 및 d3은 그대로 유지됩니다. allMethodsDelegate를 호출하면 세 개의 메서드가 순서대로 모두 호출됩니다. 대리자에서 참조 매개 변수를 사용하는 경우에는 각각의 세 메서드에 순서대로 참조가 전달되며, 한 메서드에서 참조를 변경하면 다음 메서드에서 그 내용이 인식됩니다. 메서드 중 하나가 메서드 내에서 catch되지 않은 예외를 throw하면, 대리자의 호출자에게 이 예외가 전달되고 호출 목록에 있는 이후의 메서드는 호출되지 않습니다. 대리자에 반환 값이나 out 매개 변수가 있는 경우에는 마지막으로 호출된 메서드의 반환 값 및 매개 변수가 반환됩니다. 호출 목록에서 메서드를 제거하려면 빼기 연산자('-') 또는 빼기 할당 연산자('-=')를 사용합니다. 예를 들면 다음과 같습니다.

//remove Method1
allMethodsDelegate -= d1;

// copy AllMethodsDelegate while removing d2
Del oneMethodDelegate = allMethodsDelegate - d2;

대리자 형식은 System.Delegate에서 파생되었으므로 이 클래스에 정의된 메서드 및 속성을 대리자에 대해 호출할 수 있습니다. 예를 들어, 대리자의 호출 목록에 있는 메서드의 개수를 확인하려면 코드를 다음과 같이 작성합니다.

int invocationCount = d1.GetInvocationList().GetLength(0);

호출 목록에 하나 이상의 메서드가 있는 대리자는 System.Delegate의 서브클래스인 MulticastDelegate에서 파생됩니다. 두 클래스 모두 GetInvocationList를 지원하므로 위 코드는 두 경우에서 모두 작동합니다.

멀티캐스트 대리자는 이벤트 처리에 광범위하게 사용됩니다. 이벤트 소스 개체에서는 이 이벤트를 받도록 등록된 수신자 개체에게 이벤트 알림을 보냅니다. 이벤트를 받도록 등록하기 위해 수신자에서는 이벤트를 처리하도록 디자인된 메서드를 만든 다음 이 메서드에 대한 대리자를 만들어 이벤트 소스에 전달합니다. 이벤트가 발생하면 소스는 대리자를 호출하고, 대리자는 수신자의 이벤트 처리 메서드를 호출하고 이벤트 데이터를 전달합니다. 특정 이벤트에 대한 대리자 형식은 이벤트 소스에서 정의합니다. 자세한 내용은 이벤트(C# 프로그래밍 가이드)를 참조하십시오.

컴파일 타임에 할당된 서로 다른 형식의 두 대리자를 비교하면 컴파일 오류가 발생합니다. 대리자 인스턴스가 정적 System.Delegate 형식인 경우에는 비교할 수는 있지만 런타임에 false가 반환됩니다. 예를 들면 다음과 같습니다.

delegate void Delegate1();
delegate void Delegate2();

static void method(Delegate1 d, Delegate2 e, System.Delegate f)
{
    // Compile-time error.
    //Console.WriteLine(d == e);

    // OK at compile-time. False if the run-time type of f 
    // is not the same as that of d.
    System.Console.WriteLine(d == f);
}

참고 항목

참조

대리자(C# 프로그래밍 가이드)

대리자의 가변성 사용(C# 및 Visual Basic)

Func 및 Action 제네릭 대리자에 가변성 사용(C# 및 Visual Basic)

이벤트(C# 프로그래밍 가이드)

개념

C# 프로그래밍 가이드

대리자의 가변성(C# 및 Visual Basic)