블로그 이미지
대갈장군

calendar

1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

Notice

2017. 5. 31. 23:43 프로그래밍/C#

https://docs.microsoft.com/en-us/dotnet/csharp/async


만약 입력과 출력에 관계된 일련의 처리 (데이터 베이스 처리나 네트워크를 통한 데이터 처리와 같은 일들)를 해야 할 경우에 이른바 asynchronous programming이 필요한데 이것은 내가 사용해 본 Node JS의 기본적인 처리 방법과 같다. 


요청이 들어오면 콜백 함수를 정해두고 일이 마무리 되면 콜백을 호출하는 일련의 처리 방식을 비동기 (asynchronous) 적이다 라고 하는 것.


C#은 기본적으로 제공해주는 비동기 함수 호출을 위해 아주 유용한 라이브러리 함수가 제공되니 그것이 바로 await / async 이구나.


비동기 프로그래밍의 핵심은 Task와 Task<T> 객체인데 이들은 비동기 operation들을 모델링한다. 이 객체들은 async와 await 키워드에 의해서 지원되는데 다음과 같은 경우를 생각해 볼 수 있다.


입출력에 관계된 코드의 경우, await operation은 Task나 Task<T>를 async 함수 내부에서 리턴하게 된다.


CPU에 관계된 코드의 경우 (CPU를 background로 사용하는 코드) await는 Task.Run 함수를 사용해서 background thread를 시작하게 된다.


이 await 키워드가 바로 마법을 행하는 놈인데 이놈은 실행되는 시점에 즉시 자신을 호출한 호출자에게 컨트롤을 '양보' (yield)한다. 다른 말로 하자면 버튼을 누르는 그 즉시 버튼을 컨트롤 하는 스레드에게 우선순위를 양보함으로써 UI가 사용자에 입력에 즉각적으로 반응하도록 한다는 것. 


이제 예를 들어보자. 가장 단순하게 인터넷에서 어떤 페이지의 버튼을 눌렀을 때, async와 await를 이용해서 비동기 프로그래밍을 구현한 것이다.


C#
private readonly HttpClient _httpClient = new HttpClient();

downloadButton.Clicked += async (o, e) =>
{
    // This line will yield control to the UI as the request
    // from the web service is happening.
    //
    // The UI thread is now free to perform other work.
    var stringData = await _httpClient.GetStringAsync(URL);
    DoSomethingWithData(stringData);
};


매우 간단하게 비동기 프로그래밍을 구현했다. 다만, 위 코드에서 보다시피 GetStringAsync라고 하는 비동기 전용 함수가 미리 구현되어 있어서 사용한다는 점. 그리고 버튼의 클릭 이벤트 핸들러 추가시 async 키워드를 사용했다는 점 주의.


다음으로는 CPU에 바인딩 된 경우에 대해서 알아보면, 많은 일을 처리해야 하는 경우에 사용할 수 있는 것이 바로 비동기 프로그래밍이다.


아래의 코드가 바로 그 예제이며 버튼이 눌러 졌을 때 비동기적으로 background thread를 Task.Run()을 통해서 호출하고 사용자는 스무스하게 플레이 할 수 있도록 UI에게 컨트롤을 양보한다.


C#
private DamageResult CalculateDamageDone()
{
    // Code omitted:
    //
    // Does an expensive calculation and returns
    // the result of that calculation.
}


calculateButton.Clicked += async (o, e) =>
{
    // This line will yield control to the UI CalculateDamageDone()
    // performs its work.  The UI thread is free to perform other work.
    var damageResult = await Task.Run(() => CalculateDamageDone());
    DisplayDamage(damageResult);
};



여기서 몇가지 중요한 점이 있는데 await 키워드는 반드시 async로 정의된 함수 안에서만 쓸수 있다는 점. 상식적으로 생각해서 컴파일러에게 이 함수는 비동기 함수를 포함하고 있으니 너가 적절한 조치를 취해야 한다는 것을 미리 알려줘야 한다는 점에서 이런 룰이 적용된 것으로 보인다. 물론, 암묵적인 변환을 할수 있을지는 몰라도, 아마도 명시적인 것이 더 적합할 듯.


또 한가지 더 중요한 점으로 지적된 것이, 비동기 프로그래밍을 하는 것이 성능에 있어서 분명한 이득이 될 때 사용하라는 점. 비동기 프로그래밍은 공짜로 주어지는 것이 아니기 때문에 확실히 성능상의 이득이 있을때 적절히 사용하라는 점. 오버헤드가 있다는 말.


추가적인 예제가 아래와 같이 나와 있다. 특정 웹사이트에 접속해서 특정 단어가 몇개나 들어가 있는지 검사해서 숫자를 결과를 리턴하는 것인데 await와 async로 만들었다.


C#
private readonly HttpClient _httpClient = new HttpClient();

[HttpGet]
[Route("DotNetCount")]
public async Task<int> GetDotNetCountAsync()
{
    // Suspends GetDotNetCountAsync() to allow the caller (the web server)
    // to accept another request, rather than blocking on this one.
    var html = await _httpClient.DownloadStringAsync("http://dotnetfoundation.org");

    return Regex.Matches(html, ".NET").Count;
}


드디어 Task<T>가 그 모습을 드러냈는데 T가 바로 리턴 값의 타입이다. 아래 코드는 UWP의 경우의 코드이다.


C#
private readonly HttpClient _httpClient = new HttpClient();

private async void SeeTheDotNets_Click(object sender, RoutedEventArgs e)
{
    // Capture the task handle here so we can await the background task later.
    var getDotNetFoundationHtmlTask = _httpClient.GetStringAsync("http://www.dotnetfoundation.org");

    // Any other work on the UI thread can be done here, such as enabling a Progress Bar.
    // This is important to do here, before the "await" call, so that the user
    // sees the progress bar before execution of this method is yielded.
    NetworkProgressBar.IsEnabled = true;
    NetworkProgressBar.Visibility = Visibility.Visible;

    // The await operator suspends SeeTheDotNets_Click, returning control to its caller.
    // This is what allows the app to be responsive and not hang on the UI thread.
    var html = await getDotNetFoundationHtmlTask;
    int count = Regex.Matches(html, ".NET").Count;

    DotNetCountLabel.Text = $"Number of .NETs on dotnetfoundation.org: {count}";

    NetworkProgressBar.IsEnabled = false;
    NetworkProgressBar.Visbility = Visibility.Collapsed;
}


뭐 비슷한데 가운데 추가적으로 프로그래스 바를 보여주는 옵션이 있네. 굳이 UWP의 경우를 따로 보여줘야 했나 싶다만, 아마도 UWP를 대세로 밀고 있는 듯 한 느낌적인 느낌?


그리고, 추가적으로 여러개의 task가 완료되기를 기다릴수도 있는데 바로 Task.WhenAll과 Task.WhenAny와 같은 것을 통해서 여러개의 비동기 background job들을 관리 가능하다.


C#
public async Task<User> GetUser(int userId)
{
    // Code omitted:
    //
    // Given a user Id {userId}, retrieves a User object corresponding
    // to the entry in the database with {userId} as its Id.
}

public static Task<IEnumerable<User>> GetUsers(IEnumerable<int> userIds)
{
    var getUserTasks = new List<Task<User>>();

    foreach (int userId in userIds)
    {
        getUserTasks.Add(GetUser(id));
    }

    return await Task.WhenAll(getUserTasks);
}


Task<User> 타입을 가지는 List를 만들어서 (getUserTasks) 각각의 userID를 리스트에 추가한 다음 한꺼번에 await를 통해서 호출하고 동시에 모든 작업이 완료되면 비동기적으로 그 결과를 리턴하게 된다. 


같은 일을 LINQ를 이용하는 경우에는 다음과 같다. 


C#
public async Task<User> GetUser(int userId)
{
    // Code omitted:
    //
    // Given a user Id {userId}, retrieves a User object corresponding
    // to the entry in the database with {userId} as its Id.
}

public static async Task<User[]> GetUsers(IEnumerable<int> userIds)
{
    var getUserTasks = userIds.Select(id => GetUser(id));
    return await Task.WhenAll(getUserTasks);
}


훨씬 간결한 형태의 코드가 가능하다. LINQ의 파워가 느껴진다... 포스가 느껴진다... 



마지막으로 중요한 몇가지 포인트들이 있는데,


1. async 함수는 반드시 최소한 한개의 await 키워드를 함수 내부에 가지고 있어야 한다. 없어도 컴파일은 되지만 효율이 겁나 떨어진다. 불필요한 async 남발은 피하라는 것.


2. async void는 반드시 event handler에만 사용하라. event의 경우 리턴값이 없으므로 Task나 Task<T>를 쓸 필요가 없고 그런 경우에는 async void를 쓰면 된다. 다만 이 경우에 컴파일러 입장에서는 몇몇 이유로 검증하는 것이 어렵기 때문에 최대한 async void를 사용하는 횟수를 줄여서 필요한 경우에만 쓰고 남발하지 말라는 것. 


3. LINQ와 async를 함께 쓰는 것은 매우 포스가 있지만 중요한 점은 LINQ 자체가 구현 되어 있는 방식이 async와 맞지 않는 부분도 있고 잘못 쓰게 되면 데드락이 걸리기 때문에 정확히 알고 있는 경우 아니면 되도록이면 함께 쓰지 말라.




'프로그래밍 > C#' 카테고리의 다른 글

C# 이벤트 - 객체 지향적 메시지  (0) 2010.06.25
C# - 제너릭 (Generic) 에 대한 질문  (0) 2010.06.18
Covariance and Contravariance  (0) 2010.06.18
델리게이트 - Delegate  (0) 2010.06.17
posted by 대갈장군
2010. 6. 25. 02:37 프로그래밍/C#
이벤트, 이벤트 핸들러. 이 두 단어는 API와 C++을 공부한 나에게는 아주 익숙하게 들린다. C#이 아닌 다른 언어에서는 이벤트란 API에서 비동기적으로 특정한 호출을 일으키는 명령을 의미하고 이벤트 헨들러는 그것을 받는 핸들러를 의미한다라고 아주 자연스럽게 이해가 된다.

하지만 C#에서 이벤트와 이벤트 핸들러는 좀 다른 의미다. 겹쳐지는 의미가 있기는 한데 바로 '비동기적 호출'이라는 점이다. C#에서 이벤트는 호출되어야 하는 함수의 목록을 의미한다. 중요한 것은 '목록'이라는 건데 이건 다수가 될 수 있다는 점이다. 즉, 멀티캐스트라는 것...

즉, 이벤트는 멀티캐스트 델리게이트로 구현되며 호출되어야 하는 함수 목록을 순차적으로 가진다... 그리고 이벤트에 의해 호출되는 함수를 '이벤트 핸들러'라고 한다. 이벤트를 핸들 하는 놈이라는 의미에서 이벤트 핸들러라 하는 갑다.

사실 C에 보면 메시지를 통해 이런 것을 구현했었다. 메시지를 특정 윈도우에 보내면 해당 윈도우의 메시지 처리 함수에서 메시지에 해당하는 함수를 구현하는 그런 방식이었다. 하지만 이것은 일단 복잡하고 비-객체 지향적이란다. 뭐, 객체 지향이네 아니네는 코딩을 어떻게 하느냐의 차이지만 C에서는 아무래도 언어 자체적인 객체 지향성이 C#에 비해 떨어진다는 의미다.

아무튼 이런 C의 메시지를 객체 지향적으로 포장한 것이 C#의 이벤트라는 것이다. 이벤트는 다음과 같이 선언한다.

엑세스지정자 event 델리게이트타입 이름;

이 위에 선언식을 보아하니 델리게이트와 좀 헷갈리지 않는가? 델리게이트의 선언은 다음과 같다.

엑세스지정자 delegate 리턴타입 이름(인수목록);

event를 선언하기 위해 사용된 델리게이트타입이라는 놈은 바로 델리게이트 타입 선언에 의해 선언된 놈이다. 즉, 예를 들면,

public delegate void MyDelegate(int a, int b);
public event MyDelegate
PrintOutEverything
MyEvent;

위와 같이 사용된다는 것... 델리게이트와 델리게이트 타입. 이 두 놈이 조금 복잡한듯 하나 위에 놈은 델리게이트의 타입 선언문이고 아래는 선언된 델리게이트 타입을 사용한다는 차이만 알면 될듯.

또 한가지 중요한 것은 이벤트 핸들러 즉, 호출 되는 함수는 리턴값을 가지지 않는다는 것. 원래 이벤트라는 것이 알려주기 위함이지 얻어오기 위함이 아니기 때문이다. 더군다나 이벤트 핸들러는 비동기적으로 호출되므로 리턴한다고 해도 받아 줄 놈이 없어졌을 수도 있다. 

그런데 자세히 보면 이벤트라는 놈은 사실 델리게이트타입을 재정의 한 것일뿐 특별한 차이점이 델리게이트와 비교해서 없다. 즉, 이벤트라는 지시자가 없더라도 델리게이트만으로도 모든 기능을 구현할 수 있다는 말이다. 사실 나도 이 부분을 MSDN 책을 보면서 '왜 두번 거쳐서 복잡하게 처리할까?' 라고 생각했던 부분인데 김상형님의 책에서 시원하게 설명해 주고 있다. 

이벤트는 델리게이트와 다르게 인수 정보를 사용자에게 보여주지 않고 감추며 멀티스레드에 안전하다는 장점이 있다. 즉, 사용자에게 보다 쉽고 안전한 방식으로 델리게이트를 사용하도록 했다는 것. 지금 보기에는 이것이 더 복잡해 보일지 모르나 비쥬얼 스튜디오의 마법사를 이용하면 이 과정이 사용자에게는 모두 숨겨지고 단 한번의 더블클릭으로 이루어 진다. 

그렇다면 실제로 이벤트를 폼에 적용하는 예를 살펴보자.

C#의 프로젝트를 폼 형태로 생성하면 메인 클래스는 반드시 Form을 상속받게 되어 있다. 이 Form 클래스에는 Paint라는 이벤트가 멤버로 들어가 있는데 바로 다음과 같이 선언되어 있다.

public event PaintEventHandler Paint;

여기서 PaintEventHandler가 바로 델리게이트 타입이다. 이 델리게이트 타입의 선언을 따라가보면 다음과 같이 선언 되어 있다.

public delegate void PaintEventHandler(Object sender, PaintEventArgs e);

보시다시피 리턴값이 없는 델리게이트 선언문이다. 고로 사용자는 위의 인수를 가지는 함수를 생성하여 Paint 이벤트에 대입만 해주면 화면 그리기 명령을 손쉽게 수행할 수 있는 것이다. 

이 방법 외에 윈도우에 그림을 그리는 방법이 한가지 더 있는데 바로 OnPaint 가상 함수를 재정의 하는 것이다. 이 경우에는 Control (부모)에 정의 되어 있는 OnPaint() 함수를 오버라이드 하는 것이다. 이렇게 하면 이벤트 핸들러를 사용할 필요가 없지. 

그렇다면 이 둘의 차이는? 만약 이 둘다 정의해 놓고 호출해 보면 오버라이딩 된 함수가 먼저 호출된다. 즉, OnPaint() 가상함수가 우선순위가 높다는 것이다.

헌데 막상 비주얼 스튜디오에서 더블클릭해서 이벤트 추가를 해보면 오버라이딩을 하지 않고 이벤트 핸들러를 사용하는 것을 알수 있다. 그 이유는 상속을 안받아도 되기 때문. 가상 함수를 오버라이딩 시킬려면 해당 함수를 가진 부모를 상속받아야 하는 불편함이 있다. 그에 비해 이벤트 핸들러는 델리게이트를 이용하므로 타입과 인수목록만 맞으면 자식 부모 관계 없이 호출 가능하다는 장점이 있다. 고로 이벤트 핸들러의 승리!

1. 이벤트의 정의는?
2. 이벤트 핸들러는 리턴값을 가지지 않는데 왜 그럴까?
3. 델리게이트와 이벤트는 어떻게 다른가?
4. 왜 Visual Studio에서 마법사로 함수를 추가하면 가상함수 오버라이딩 대신 이벤트 핸들러를 사용하는가?


'프로그래밍 > C#' 카테고리의 다른 글

Asynchronous programming  (0) 2017.05.31
C# - 제너릭 (Generic) 에 대한 질문  (0) 2010.06.18
Covariance and Contravariance  (0) 2010.06.18
델리게이트 - Delegate  (0) 2010.06.17
posted by 대갈장군
2010. 6. 18. 10:04 프로그래밍/C#
1. 제네릭이나 템플릿을 사용하는 이유? 역효과는?


2. 제네릭 타입 구체화 (Generic Type Instantiation) 이란?


3. 제네릭의 타입을 호출시 정확히 알려줘야 할 경우는 어떤 경우인가?


4. 제약 조건 (where)에서 가장 유용하다고 생각되는 것은 base, 즉, base를 상속받은 클래스이어야 한다는 조건인데 왜 그럴까?


5. 제네릭은 C++의 템플릿과 유사한데 C++과 무엇이 다른가?


6. 일반 컬렉션 (ArrayList)를 사용하게되면 예상되는 문제점은 없는가?


7. 일반 컬렉션 대신 제네릭 컬렉션 (List<T>)를 사용하게되면 왜 더 안전한 코딩인가?


'프로그래밍 > C#' 카테고리의 다른 글

Asynchronous programming  (0) 2017.05.31
C# 이벤트 - 객체 지향적 메시지  (0) 2010.06.25
Covariance and Contravariance  (0) 2010.06.18
델리게이트 - Delegate  (0) 2010.06.17
posted by 대갈장군
2010. 6. 18. 00:43 프로그래밍/C#
델리게이트에 대해서 공부하다 보니 공변성 (Covariance)반공변성 (Contravariance) 에 대한 이야기가 나왔다.

공변성과 반공변성... 뭔 소리인가... 라고 생각한다면 당신은 정상인...

수학 쫌 했다는 분들은 알아들을 수도 있겠다만 나는 처음 이 두 단어를 접하고 언어의 한계성을 느꼈다...

아무튼, 공변성과 반공변성은 사실 같은 원리에 근거한 같은 법칙을 말한다. 여기서 같은 원리란, 부모는 자식을 받아들일수 있으나 자식은 부모를 받아들일수 없다는 원칙이다. 쓰다보니 이런 패륜적인 원리일 줄이야... 헐.

일반적으로 자식 클래스는 부모 클래스로 부터 상속을 받아 여러가지 함수 및 멤버 변수를 추가하게 된다. 자, 그렇다면 자식은 양적으로 부모 보다 더 '많이' 가지고 있다. 반면 부모 클래스는 여러 자식들에게 공통으로 나누어 주어야 하므로 '적게' 가지고 있다.

부모/자식 클래스라는 헷갈리는 말 대신, 많다/적다로 표현하면 훨씬 더 이해가 빠르게 될 것이다. 그렇다면 부모 클래스에 자식 클래스를 대입하면 어떻게 되나? 다른 말로 적은 것 (부모)에 많은 것 (자식)을 넣게되면 자식이 가진 몇몇 함수 및 변수는 잘려 나가겠지만 그래도 잘라내면 그만이므로 문제가 없다.

헌데 반대로 자식에 부모를 대입한다면, 즉, 많은 것 (자식)에게 적은 것 (부모)를 대입한다면 문제가 생기는데 왜냐면 부모는 자식이 가지고 있는 것들을 가지고 있지 않기 때문에 '비어있는 공간'이 생기게 되고 이는 호출 오류를 일으킨다. 왜? 없는 것을 호출하니까...

그래서 부모(적은 것)는 자식(많은 것)을 받아들이지만 그 반대는 성립하지 않는다는 것이다.

함수를 살펴보면 "리턴타입 함수이름(인수)" 이런 형태다. 여기서 리턴 타입과 인수가 바로 '대입'이 일어나는 두 곳이다. 자 여기서 바로 공변성과 반공변성이 등장한다... 

일단 리턴타입의 경우 공변성을 가지는데 다른말로 풀어 쓰자면 위에서 설명한 기본 원리인 '부모는 자식을 받지만 그 반대는 성립하지 않는다'는 원칙을 눈으로 볼때 그대로 따른다는 의미다. 

즉, 선언한 델리게이트의 리턴 타입이 부모 클래스라면 자식 클래스 및 부모 클래스 타입을 리턴 타입으로 가지는 모든 함수를 받아준다는 이야기다. 당연히 그 반대는 안되죠오..

이제 남은 것은 '인수'인데, 여기서 이른바 '
반공변성
반공변성'
이 나온다. 말은 참 어려운것 같지만 한번만 풀어서 생각하면 같은 원리다. 이 반공변성은 위에서 말한 리턴 타입의 경우와 정 반대로 동작한다는 의미에서 붙여진 이름이다.

이것은 언제 '인수'가 대입 되는가를 생각해 보면 이해가 된다. 리턴타입의 경우 델리게이트 객체에 함수를 대입할 때 바로 '대입'이 일어나지만 '인수'는 델리게이트 객체에 함수를 대입할 때가 아니라 그 후에 대입된 함수를 호출 할 때 '대입'이 일어난다. 

고로 리턴 타입과는 달리 인수는 비교해야 하는 시점이 호출할 때이므로 델리게이트 객체에 함수를 대입할 때의 인수가 호출 시점의 인수보다 '부모'이어야 한다. 이것이 코드에서 보았을 때는 공변성과 정반대로 보이게 되므로 이름을 반공변성이라고 붙였다만 사실 알고보면 똑같은 원리에 근거한 것이다.

정리하자면 다음과 같이 말할 수 있다.

리턴타입 델리게이트객체(인수) = 리턴타입 함수객체(
);

위의 공식을 보면 좌측 리턴 타입이 우측 리턴 타입보다 큰데 이것은 좌측 리턴 타입이 우측 리턴 타입을 받아준다는 의미고 마찬가지로 우측 인수가 좌측 인수보다 크므로 받아준다는 의미다. 

이런 공변성과 반공변성을 잘 알고 있으면 델리게이트의 활용에 큰 도움이 될듯..







'프로그래밍 > C#' 카테고리의 다른 글

Asynchronous programming  (0) 2017.05.31
C# 이벤트 - 객체 지향적 메시지  (0) 2010.06.25
C# - 제너릭 (Generic) 에 대한 질문  (0) 2010.06.18
델리게이트 - Delegate  (0) 2010.06.17
posted by 대갈장군
2010. 6. 17. 23:31 프로그래밍/C#
델리게이트는 C#에 등장하는 새로운 함수 포인터다. 델리게이트의 사전적 의미는 '대표자'라는 의미로 명사일때와 동사일때 발음이 차이가 있으며... 아차차, 주제가 이게 아니지. 이놈의 토플 공부 습관은 사라지지가 않는다. -_-

그렇다면 우선 델리게이트 선언 형식을 살펴보자. 간단하다.

액세스지정자 delegate 리턴타입 이름(인수목록);

이것은 C++에서 사용하는 함수 포인터의 선언 형식보다 훨씬 더 가독성이 좋은 편이다. 

일단, C++과 비교해서 C#의 함수 포인터인 델리게이트의 장점 및 차이점을 나열해 보자.

  • C++의 함수 포인터에 비해 타입 체크가 훨씬 더 엄격하므로 잘못된 대입을 미리 막는다.
  • Delegate는 함수 포인터와 다르게 '클래스 타입'이다. 
  • 인수 목록에 반드시 이름이 필요하다.

  • 사실 위 3가지 차이점 중에 2번이 가장 크고 중요한 차이라고 개인적으로 생각한다. delegate가 클래스 타입이라고 했는데 이는 즉, delegate가 클래스를 선언하는데 사용되는 일종의 명시자라는 말이다. 자, 클래스라면 생성자를 사용해서 객체화를 하여 사용해야 한다. 고로 delegate를 사용하기 위해서는 delegate 클래스 타입으로 임의의 객체의 선언 및 생성자를 이용한 객체화가 요구된다.

    추가로 delegate는 엄연히 독립 타입이므로 어디에나 선언 가능하며 클래스 내부에 선언되면 액세스 지정자 (public, private) 를 줄수가 있다. 만약 외부에 전역으로 선언되면 엑세스 지정자는 굳이 필요하지 않다.

    다음과 두 함수를 보면 인수목록이 같고 리턴 타입이 같으므로 delegate를 쓸수 있다.

    public static void MyFunction1(int input) { Console.WriteLine("MyFunction1" + input); }
    public static void MyFunction2(int input) { Console.WriteLine("MyFunction2" + input); }

    이 두 함수를 가리키기 위해 델리게이트 타입의 클래스를 전역으로 선언한다면 다음과 같이 선언 할 수 있다.

    delegate void MyDele(int a);

    그리고 이제 이 선언된 MyDele 클래스의 객체를 객체화 (생성자를 이용한 구현) 를 하기만 하면 포인터 처럼 바로 사용가능하다.

    MyDele mDele;
    mDele = new MyDele(MyFunction1); //또는 mDele = MyFunction1;
    mDele(50);
    mDele = new MyDele(MyFunction2); //또는 mDele = MyFunction2;
    mDele(100);

    다시 한번 강조하지만 delegate는 클래스 타입이므로 delegate를 사용하기 위해서는 우선 delegate 클래스 타입으로 특정한 리턴 타입과 인수 목록을 가지는 클래스(MyDele)를 생성하고 이 클래스의 객체(mDele)를 생성하여 생성자로 가리킬 함수(MyFunction1 또는 MyFunction2)를 대입하면 만들어진 객체가 바로 그 함수가 된다.

    일단 C++ 방법에 비해 복잡해 보이지만 훨씬 더 '
    세련된
    세련된' 방식이다. 참고로 new로 선언한 후 해제하지 않았는데 우리 뒤에는 항상 Garbage Collector 가 알아서 쓰레기를 수거해 가는 것을 잊지 말자. +.+

    함수 포인터나 델리게이트의 장점은 바로 같은 인수로 리턴 타입을 가지는 여러개의 함수를 돌아가면서 가리킬 수 있다는 것인데 이런 장점은 사실 확 와닿지가 않는다. 이것의 장점을 몸으로 느끼기 위해서는 프로그램이 조금 커야 하며 설계 부터 상속을 염두해두고 만들어야 하며 가상함수들이 즐비해야 비로서 '아~ 이래서 델리게이트가 무려 챕터 하나를 차지하는구나...' 하고 느낄것이다.

    마지막으로 C++의 함수 포인터와 다른 큰 차이점으로 델리게이트는 다른 클래스에 속한 메서드를 가리킬 수 있다는 점이다. C++의 함수 포인터의 경우 임의의 클래스에 선언되어 있을 경우 그 해당 클래스 내부에 있는 메서드 만을 가릴킬 수 있었는데 델리게이트는 그것을 극복했다. 

    이것도 사실 '화악~' 마음에 와 닿지는 않지만 C++의 함수 포인터보다는 훠~얼씬 더 좋다는 것 정도는 알겠다. 

    정리하자면 C++의 함수 포인터는 단순한 변수였다. 말그대로 주소를 저장하는 포인터 였으나 C#에서 등장한 델리게이트라는 놈은 함수 포인터보다 훨씬더 가독성이 좋으며 독립적인 클래스 타입으로써 객체를 생성하여 사용한다는 점이 큰 차이다... 암튼, 당연히 델리게이트가 함수 포인터 보다는 좋다는 이야기지...

    더 많은 이야기를 하고 싶지만 코드가 들어가면 이야기가 너무 길어질 것 같아서 오늘은 여기까지만... 다음엔 공변성과 반공변성에 대해서 토크를 해야 할 듯...



    '프로그래밍 > C#' 카테고리의 다른 글

    Asynchronous programming  (0) 2017.05.31
    C# 이벤트 - 객체 지향적 메시지  (0) 2010.06.25
    C# - 제너릭 (Generic) 에 대한 질문  (0) 2010.06.18
    Covariance and Contravariance  (0) 2010.06.18
    posted by 대갈장군
    prev 1 next