[C#] 22. 델리 게이트(delegate)


Study/C#  2021. 9. 6. 18:20

안녕하세요. 명월입니다.


이 글은 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) 사용법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.