안녕하세요. 명월입니다.
이 글은 C#의 델리게이트(delegate) 사용법에 대한 글입니다.
C#의 델리게이트는 대리자라는 뜻으로 C++의 함수 포인터와 비슷한 개념을 가지고 있는 키워드입니다. 즉, 함수 포인터란 함수식(function)을 인스턴스의 포인터처럼 인식을 해서 변수에 값을 저장하거나 파라미터로 넘겨서 실행하는 대리로 실행하는 역할을 합니다.
using System;
namespace Example
{
class Program
{
// 델리게이트 선언
delegate void PrintDelegate(String str);
// 출력 함수
public void Print(String str)
{
Console.WriteLine(str);
}
public Program()
{
// 델리게이트에 메소드를 넣는다.
// 델리게이트의 반환타입, 파라미터가 일치해야한다.
PrintDelegate pd = new PrintDelegate(Print);
// 델리 게이트 실행
pd("Hello World");
}
static void Main(string[] args)
{
new Program();
// 아무 키나 누르시면 종료합니다.
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
위 예제에서 Print함수를 델리게이트를 사용해서 포인터로 인스턴스의 포인터로 변환하고 pd의 변수를 인스턴스의 값처럼 사용할 수 있습니다.
실행은 델리게이트의 pd 변수는 그냥 함수 호출하듯이 파라미터 값을 넣으면 실행되어서 나오는 것을 확인할 수 있습니다.
위의 식은 델리게이트를 설명하기 위한 아주 심플하게 사용한 것이고 실제로 사용하는 것에 대해서는 변수처럼 사용할 수 있는 장점이 있습니다.
using System;
using System.Collections.Generic;
namespace Example
{
// 인터페이스
interface INode
{
// 함수 선언
void Print(String str);
}
// 델리게이트가 있는 클래스
class Example : INode
{
// 반환값이 void에 String 파라미터가 하나있는 델리게이트
// 델리게이트를 외부에서 사용하기 위해서는 접근 제한자를 public으로 설정해야 한다.
public delegate void PrintDelegate(String str);
// 함수 포인트를 넣기 위한 리스트
private List<PrintDelegate> list = new List<PrintDelegate>();
// 함수 추가
public void AddDelegate(PrintDelegate func)
{
list.Add(func);
}
// Print 함수를 실행하면 리스트에 저장된 함수를 실행한다.
public void Print(String str)
{
// 반복문으로 리스트에 저장된 함수를 가져온다.
foreach (var item in list)
{
// 함수를 실행한다.
item(str);
}
}
}
// Node1 클래스
class Node1 : INode
{
// Print 함수
public void Print(String str)
{
// 콘솔 출력
Console.WriteLine("Node1 print - " + str);
}
}
// Node2 클래스
class Node2 : INode
{
// Print 함수
public void Print(String str)
{
// 콘솔 출력
Console.WriteLine("Node2 print - " + str);
}
}
class Program
{
// 실행 함수
static void Main(string[] args)
{
// 델리게이트가 있는 클래스의 인스턴스를 생성
Example example = new Example();
// Node1 클래스의 인스턴스를 생성
INode node1 = new Node1();
// Node2 클래스의 인스턴스를 생성
INode node2 = new Node2();
// 델리게이트가 있는 클래스에 Node1클래스의 Print 함수를 등록
example.AddDelegate(node1.Print);
// 델리게이트가 있는 클래스에 Node2클래스의 Print 함수를 등록
example.AddDelegate(node2.Print);
// 델리게이트에 등록되어 있는 함수에 Test값을 넣어 실행
example.Print("Test");
// 아무 키나 누르시면 종료합니다.
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
위 예제를 보면 Example클래스와 Node1, Node2 클래스를 INode 인터페이스를 상속 받았습니다.
위 형식은 디자인 패턴 중의 합성 패턴입니다. Example 클래스에 Node1 클래스의 Print 함수와 Node2 클래스의 Print 함수를 넣어 Example 클래스의 Print함수를 실행하면 동시에 실행되는 패턴입니다.
위 예제는 제가 List에 델리게이트를 넣어서 함수 포인터를 관리하도록 했습니다.
그러나 이 델리게이트는 자체적으로 리스트 기능이 있습니다. +=와 -=의 대입 연산자를 통해서 추가, 제거를 할 수 있습니다.
using System;
using System.Collections.Generic;
namespace Example
{
// 인터페이스
interface INode
{
// 함수 선언
void Print(String str);
}
// 델리게이트가 있는 클래스
class Example : INode
{
// 반환값이 void에 String 파라미터가 하나있는 델리게이트
// 델리게이트를 외부에서 사용하기 위해서는 접근 제한자를 public으로 설정해야 한다.
public delegate void PrintDelegate(String str);
// 함수 포인트를 넣기 위한 리스트
private PrintDelegate list;
// 함수 추가
public void AddDelegate(PrintDelegate func)
{
list += func;
}
// 함수 제거
public void RemoveDelegate(PrintDelegate func)
{
list -= func;
}
// 리스트에 등록된 함수를 실행
public void Print(String str)
{
list(str);
}
}
// Node1 클래스
class Node1 : INode
{
// Print 함수
public void Print(String str)
{
// 콘솔 출력
Console.WriteLine("Node1 print - " + str);
}
}
// Node2 클래스
class Node2 : INode
{
// Print 함수
public void Print(String str)
{
// 콘솔 출력
Console.WriteLine("Node2 print - " + str);
}
}
class Program
{
// 실행 함수
static void Main(string[] args)
{
// 델리게이트가 있는 클래스의 인스턴스를 생성
Example example = new Example();
// Node1 클래스의 인스턴스를 생성
INode node1 = new Node1();
// Node2 클래스의 인스턴스를 생성
INode node2 = new Node2();
// 델리게이트가 있는 클래스에 Node1클래스의 Print 함수를 등록
example.AddDelegate(node1.Print);
// 델리게이트가 있는 클래스에 Node2클래스의 Print 함수를 등록
example.AddDelegate(node2.Print);
// 델리게이트에 등록되어 있는 함수에 Test값을 넣어 실행
example.Print("Test");
// 델리게이트에 node1.Print함수를 제거
example.RemoveDelegate(node1.Print);
example.Print("Hello world");
// 아무 키나 누르시면 종료합니다.
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
위 예제에서는 2번째의 List클래스 대신에 델리게이트로 함수식을 추가,삭제할 수 있습니다.
최근에는 C# 코드에서 람다식(lambda)을 쉽게 볼 수 있는데, 이 람다식이 델리게이트를 기반으로 두고 만들어졌습니다.
using System;
using System.Collections.Generic;
namespace Example
{
// 델리게이트가 있는 클래스
class Example
{
// 반환값이 void에 String 파라미터가 하나있는 델리게이트
// 델리게이트를 외부에서 사용하기 위해서는 접근 제한자를 public으로 설정해야 한다.
public delegate void PrintDelegate(String str);
// 함수 포인트를 넣기 위한 리스트
private PrintDelegate list;
// 함수 추가
public void AddDelegate(PrintDelegate func)
{
list += func;
}
// 함수 제거
public void RemoveDelegate(PrintDelegate func)
{
list -= func;
}
// 리스트에 등록된 함수를 실행
public void Print(String str)
{
list(str);
}
}
class Program
{
// 실행 함수
static void Main(string[] args)
{
// 델리게이트가 있는 클래스의 인스턴스를 생성
Example example = new Example();
example.AddDelegate((str) =>
{
Console.WriteLine("Lambda - " + str);
});
// 델리게이트에 등록되어 있는 함수에 Test값을 넣어 실행
example.Print("Test");
// 아무 키나 누르시면 종료합니다.
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
람다식은 익명 함수의 뜻으로 함수에 이름이 없는 함수를 이야기합니다. 이 람다식은 함수명이 존재하지 않기 때문에 함수 포인터만 존재하게 됩니다.
즉, 람다식을 사용하기 위해서는 이 델리게이트로 함수의 포인터를 가지고 있어야 한다는 뜻입니다.
이 람다식은 람다식을 설명할 때 좀 더 자세하게 설명하겠습니다.
여기까지 C#의 델리게이트(delegate) 사용법에 대한 글이었습니다.
궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.
'Study > C#' 카테고리의 다른 글
[C#] 26. var 키워드와 dynamic 키워드 (0) | 2021.09.10 |
---|---|
[C#] 25. 예외 처리(try ~ catch)하는 방법 (0) | 2021.09.09 |
[C#] 24. 이벤트(event) 키워드 사용법 (0) | 2021.09.08 |
[C#] 23. 람다식(익명 함수)과 Action, Func 함수 사용법, 그리고 클로저(Closure) (0) | 2021.09.07 |
[C#] 21. 인덱서(Indexer)를 사용하는 방법 (0) | 2020.07.31 |
[C#] 20. 프로퍼티 (Property) (0) | 2020.07.30 |
[C#] 19. 객체 지향(OOP) 프로그래밍의 4대 원칙(캡슐화, 추상화, 상속, 다형성) (0) | 2020.07.28 |
[C#] 18. 열거형(enum)을 사용하는 방법 (2) | 2020.07.27 |