C# 개념잡기 - Delegate


델리게이트

  • Delegate : 위임, 대리자 ---> 자신에게 전달된 Method를 대신 실행함,
  • 키워드는 delegate, new 연산자로 인스턴스를 생성함
  • return type과 paramter는 대신 처리하는 Method에 종속적임
  • 델리게이트의 인스턴스를 호출한다는 것은 ---> “포인터”가 가르키는 Method를 실행한다는 것
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace Delegate 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
            DeleageClass dc = new DeleageClass(); 
            dc.TestMethod(); 
        } 
    } 

    class DeleageClass 
    { 
        // 델리게이트 선언 - paramater, return type은 대신할 놈과 같다 
        delegate void SimpleDeleage(string para); 

        // 델리게이트가 대신할 메소드 
        private void SimpleCallbackMethod(string para) 
        { 
            Console.WriteLine(para); 
        } 

        // 델리게이트 테스트 메소드 
        public void TestMethod() 
        { 
            SimpleDeleage sd = new SimpleDeleage(SimpleCallbackMethod); 
            sd("테스트에요"); 
            sd.Invoke("테스트에요"); 
        } 
    } 
}

동기화 vs 비동기화

사용자 삽입 이미지

  • BeginInvoke / EndInvoke : 비동기 방식, 내부적으로 쓰레드를 생성
  • Invoke : 동기방식, 이 작업이 끝나야 다른 작업을 할 수 있음

멀티캐스트 델리게이트

  • 멀티캐스트 델리게이트 : 하나의 delegate 로 여러개의 Method를 실행
  • MulticastDelegateClass() : System.Deleage.Combine으로 Method들을 Chain 으로 “순차적으로” 관리
  • MulticastDelegateClass2() : MulticastDelegate.GetInvocationList() 를 사용하여 Chain 목록을 가져와 return 검사할 수도 있음
  • MulticastDelegateClass3() : +, –, +=, –=  을 사용하여 관리함 ---> 이벤트에서도 멀티캐스트 델리게이트 개념을 사용함
class MulticastDelegateClass 
{ 
    // 델리게이트 선언 
    delegate void ProgrammingDelegate(); 

    // 델리게이트 메소드 구현 - parameter, return type 이 델리게이트와 같음 
    private void 분석() { Console.WriteLine("분석"); } 
    private void 설계() { Console.WriteLine("설계"); } 
    private void 개발() { Console.WriteLine("개발"); } 
    private void 테스트() { Console.WriteLine("테스트"); } 

    // 테스트 
    public void MulticastDelegateTest() 
    { 
        ProgrammingDelegate pd_분석 = new ProgrammingDelegate(분석); 
        ProgrammingDelegate pd_설계 = new ProgrammingDelegate(설계); 
        ProgrammingDelegate phase1 = (ProgrammingDelegate)System.Delegate.Combine(pd_분석, pd_설계); 

        ProgrammingDelegate pd_개발 = new ProgrammingDelegate(개발); 
        ProgrammingDelegate phase2 = (ProgrammingDelegate)System.Delegate.Combine(phase1, pd_개발); 

        ProgrammingDelegate pd_테스트 = new ProgrammingDelegate(테스트); 
        ProgrammingDelegate phase3 = (ProgrammingDelegate)System.Delegate.Combine(phase2, pd_테스트); 

        phase3(); 
    } 
}



class MulticastDelegateClass2 
{ 
    // 델리게이트 선언 
    delegate bool ProgrammingDelegate(); 

    // 델리게이트 메소드 구현 - parameter, return type 이 델리게이트와 같음 
    private bool 분석() { Console.WriteLine("분석"); return true; } 
    private bool 설계() { Console.WriteLine("설계"); return true; } 
    private bool 개발() { Console.WriteLine("개발 못했어요"); return false; } 
    private bool 테스트() { Console.WriteLine("테스트"); return true; } 

    // 테스트 
    public void MulticastDelegateTest() 
    { 
        ProgrammingDelegate pd_분석 = new ProgrammingDelegate(분석); 
        ProgrammingDelegate pd_설계 = new ProgrammingDelegate(설계); 
        ProgrammingDelegate phase1 = (ProgrammingDelegate)System.Delegate.Combine(pd_분석, pd_설계); 

        ProgrammingDelegate pd_개발 = new ProgrammingDelegate(개발); 
        ProgrammingDelegate phase2 = (ProgrammingDelegate)System.Delegate.Combine(phase1, pd_개발); 

        ProgrammingDelegate pd_테스트 = new ProgrammingDelegate(테스트); 
        ProgrammingDelegate phase3 = (ProgrammingDelegate)System.Delegate.Combine(phase2, pd_테스트); 

        // 요놈은 마지막 "pd_테스트"가 true 를 리턴하므로 항상 ture가 됨 
        Console.WriteLine(phase3()); 
        Console.WriteLine("-------------------------------------------"); 

        // Delegate List를 통해 return 값을 검사해서 중지하는 예제 
        System.Delegate[] deleagteList = phase3.GetInvocationList(); 
        bool result = false; 

        foreach (ProgrammingDelegate prg in deleagteList) 
        { 
            result = prg(); 
            if(result == false) 
            { 
                Console.WriteLine("{0} 단계에서 실패하여 중단합니다.", prg.Method.Name); 
                break; 
            } 
        } 
        Console.WriteLine("-------------------------------------------"); 
    } 
}



class MulticastDelegateClass3 
{ 
    // 델리게이트 선언 
    delegate bool ProgrammingDelegate(); 

    // 델리게이트 메소드 구현 - parameter, return type 이 델리게이트와 같음 
    private bool 분석() { Console.WriteLine("분석"); return true; } 
    private bool 설계() { Console.WriteLine("설계"); return true; } 
    private bool 개발() { Console.WriteLine("개발 못했어요"); return false; } 
    private bool 테스트() { Console.WriteLine("테스트"); return true; } 

    // 테스트 
    public void MulticastDelegateTest() 
    { 
        ProgrammingDelegate pd_분석 = new ProgrammingDelegate(분석); 
        ProgrammingDelegate pd_설계 = new ProgrammingDelegate(설계); 
        ProgrammingDelegate pd_개발 = new ProgrammingDelegate(개발); 
        ProgrammingDelegate pd_테스트 = new ProgrammingDelegate(테스트); 

        ProgrammingDelegate pd_프로젝트 = new ProgrammingDelegate(pd_분석); 
        pd_프로젝트 += pd_설계; 
        pd_프로젝트 += pd_개발 + pd_테스트; 
        pd_프로젝트 += pd_테스트; 
        pd_프로젝트 -= pd_테스트; 

        pd_프로젝트(); 
        Console.WriteLine("-------------------------------------------"); 
    } 
}

델리게이트 비동기 호출

  • IAsyncResult 인터페이스를 사용하여 비동기 호출을 하고 모니터링함.
class AsyncDelegateClass 
{ 
    // 델리게이트 선언 
    delegate void AsyncDelegate(out int threadID); 

    // 델리게이트 메소드 구현 
    private void CallByAsyncDelegateMethod(out int threadID) 
    { 
        // IAsyncResult는 비동기 실행을 모니터함 
        // IAysncResult의 IsComplete를 시험하기 위해 일부러 지연 
        System.Threading.Thread.Sleep(1); 

        // out 키워드는 참조로 전달됨을 의미 
        // ref와는 달리 out은 초기화가 필요없고, 메소드에서 반드시 값이 할당되어야 함. 
        threadID = System.Threading.Thread.CurrentThread.ManagedThreadId; 
        Console.WriteLine("비동기 메소드"); 
    } 

    // 테스트 
    public void AsyncDelegateTest() 
    { 
        AsyncDelegate ad = new AsyncDelegate(CallByAsyncDelegateMethod); 
        int currentThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId; 
        int asyncThreadID; 

        // 비동기 호출 시작 & 완료 #1 
        //Console.WriteLine("비동기 호출 시작"); 
        //IAsyncResult asyncResult = ad.BeginInvoke(out asyncThreadID, null, null); 
        //ad.EndInvoke(out asyncThreadID, asyncResult); 
        //Console.WriteLine("비동기 호출 완료"); 
        //Console.WriteLine("메인 쓰레드{0}, 비동기 호출된 쓰레드{1}", currentThreadID, asyncThreadID); 
        //Console.WriteLine("-------------------------------------------"); 

        // 비동기 호출 시작 & 완료 #2 
        Console.WriteLine("비동기 호출 시작"); 
        IAsyncResult asyncResult = ad.BeginInvoke(out asyncThreadID, null, null); 
        while(asyncResult.IsCompleted == false) 
        { 
            Console.WriteLine("메소드 안 끝났다. 대기중"); 
        } 
        Console.WriteLine("메소드 끝났다."); 
        ad.EndInvoke(out asyncThreadID, asyncResult); 
        Console.WriteLine("비동기 호출 완료"); 
        Console.WriteLine("메인 쓰레드{0}, 비동기 호출된 쓰레드{1}", currentThreadID, asyncThreadID); 
        Console.WriteLine("-------------------------------------------"); 
    } 
}

콜백 메소드 호출

  • Delegate 비동기 호출시 Callback Method 사용가능
  • ~후에(Back) 호출하다(Call)
  • BeginInvoke 메소드에 Callback Method 실행을 위한 AsyncCallback 델리게이트를 전달
// 콜백 메소드 
private void CallbackMethod(IAsyncResult asyncResult) 
{ 
    // 콜백 메소드에 전달된 AsyncDelegate 델리게이트 
    AsyncDelegate ad = (AsyncDelegate)asyncResult.AsyncState; 
    int callbackThreadID; 

    // 비동기 호출 완료 
    ad.EndInvoke(out callbackThreadID, asyncResult); 
    Console.WriteLine("콜백 메소드 비동기 호출 종료"); 
} 

// 테스트 - 비동기 - 콜백메소드 
public void AsyncDelegateTest2() 
{ 
    // 델리게이트 인스턴스 생성 
    AsyncDelegate ad = new AsyncDelegate(CallByAsyncDelegateMethod); 
    int asyncThreadID; 

    // 콜백 메소드 비동기 호출 
    Console.WriteLine("콜백 메소드 비동기 호출 시작"); 
    IAsyncResult ar = ad.BeginInvoke(out asyncThreadID, new AsyncCallback(CallbackMethod), ad); 
    Console.WriteLine("-------------------------------------------"); 
}

제네릭을 이용한 델리게이트

// 델리게이트 선언 - paramater, return type은 대신할 놈과 같다 ---> T 
delegate void GenericDeleage<T>(T item); 

// 델리게이트가 대신할 메소드 
private void StringMethod(string para) { Console.WriteLine(para); } 

private void IntMethod(int para) { Console.WriteLine(para.ToString()); }

이벤트

  • .net의 이벤트 : 델리게이트를 확장한 형태로 사용됨
  • += 는 델리게이트 연산자
  • 이벤트시 호출되는 메소드는 델리게이트 포인터로 연결됨
// 인스턴스 생성 
FileSystemWatcher watcher = new FileSystemWatcher(path); 
// 이벤트 등록 
watcher.Created += new FileSystemEventHandler(watcher_Created); 
// 이벤트 실행되도록 상태값 설정 
watcher.EnableRaisingEvents = true; 
… 
// 이벤트 실행시 실행 메소드 
void watcher_Created(object sender, FileSystemEventArgs e) 
{ 
    … 
}

  • FCL(Framework Class Library)에 포함된 System.IO 네임스페이스의 코드는 아래와 같다.
  • 이벤트에 사용되는 델리게이트는 공통적으로 object와 EventArgs 클래스를 파라메터로 사용함
namespace System.IO 
{
    public delegate void FileSystemEventHandler(object sender, FileSystemEventArgs e); 

    public class FileSystemWatcher : Component, ISupportInitialize 
    { 
        public event FileSystemEventHandler Created; 
    } 
}

  • 이벤트 제공자 : 자신이 제공할 수 있는 이벤트 공개 (watcher.Created)
  • 이벤트 소비자 : 공개된 이벤트를 등록함 (+= new FileSystemEventHandler(watcher_Created); )
  • 이벤트 실행 : 이벤트에 해당하는 액션이 발생하면 제공자는 소비자가 등록한 행동을 호출 (watcher_Created를 실행함) ---> 느슨한 연결(Loosey Coupled)

광고

댓글 남기기