프로그래밍 관련/언어들의 코딩들 C++ JAVA C# 등..

[C#] delegate 델리게이트 관련

AlrepondTech 2020. 9. 10. 04:25
반응형

 

 

 

=======================

=======================

=======================

 

 

 

 

출처: https://jeong-pro.tistory.com/51

C# 델리게이트(delegate)

: C#에서 메서드를 가리킬 수 있는 타입

(C++의 함수 포인터, 함수형프로그래밍에서 일급 함수와 유사하다.)

delegate로 선언한 타입이 메서드를 가리키기 때문에

그 메서드를 직접 호출하는 것 대신에 delegate로 그 메서드를 호출할 수 있다.

- 메서드를 직접호출하면되지 왜 굳이 delegate를 사용해서 호출할까? 는 잠시 후에..


1. delegate 만들기

 delegate가 어떤 메서드를 가리키기 때문에 그 메서드와 동일한 매개변수와 리턴타입으로 선언해야한다.

선언 구문

=> 접근제한자 delegate 대상_메서드의_반환타입 타입명(대상_메서드의_매개변수들..);

(예시에선 접근제한자 생략)

 

delegate void Type1(void);  // void func1(void)

delegate int Type2(int,int); // int func2(int, int)

delegate string Type3(double); // string func3(double)

delegate float Type4(int); // float func4(int)

 

* 너무도 간단하다. 앞에 delegate를 붙이고 내가 원하는 타입이름(Type1, Type2, ..)을 쓰고 매개변수만 똑같이 맞춰주면 끝!

2. delegate 사용

delegate는 '타입' 이기 때문에 int, string, char, ... 처럼 사용한다.

Type1 F1Delegate;

F1Delegate = new Type1(func1);

Type2 F2Delegate;

F2Delegate = new Type2(func2);

Type3 F3Delegate = func3; // C# 2.0부터 사용가능

int a = 5;

처럼 그냥 메서드 넣고, 그 메서드 호출하면 된다.

F1Delegate(); , F2Delegate(5,5); , F3Delegate(3.14);

* 타입명에 관례적으로 접미사 Delegate를 붙임. 안 붙여도 상관없으나 다 이유가 있다.


간단한 실제 사용 예제


이걸 왜 쓸까? 결론부터 말하면 '콜백(CallBack)메서드'에서 활용하면 드라마틱하다.

콜백

: A라는 메서드를 호출할 때, B라는 메서드를 매개변수로 넘겨주고 A메서드에서 B 메서드를 호출하는 것

이 상황에서 A메서드를 '콜백메서드' 라고 한다. (밑의 예제에서 확인)

- 콜백은 메서드를 호출하는 것이기 때문에 실제 필요한 것은 타입이 아니라 하나의 메서드다.

따라서 타입 자체를 전달해서 실수를 유발할 여지를 남기기보다 메서드에 대한 delegate만 전달해서 이 문제를 해결할 수 있다.

이제는 원하는 메서드를 같이 매개변수로 넘겨주어 해당 기능을 수행한다.

Calc라는 메서드 A가 콜백메서드가 된다. Plus, Minus라는 메서드B를 A에서 호출하니까.


범용성을 고려한 일반화

제네릭 <T>를 이용해서 어떤 타입이든 사용가능한 메서드를 만들었다.

구현에서 object타입을 이용할 수도 있다.


delegate 체인

지금까지본 delegate 인스턴스는 하나의 메서드만 가리키고 있었는데 여러개를 가리키는 것도 가능하다.

특이하게 += 연산자를 이용해 delegate 인스턴스를 추가한다.

마찬가지로 -= 연산자를 이용해 인스턴스를 삭제할 수있다.

( 이 모든게 C# 컴파일러의 배려..... )



출처: https://jeong-pro.tistory.com/51 [기본기를 쌓는 정아마추어 코딩블로그]

 

 

=======================

=======================

=======================

 

 

 

반응형

 

728x90

 

 

 

출처: http://www.csharpstudy.com/csharp/CSharp-delegate.aspx

C# delegate는 C/C++의 함수 포인터와 비슷한 개념으로 메서드 파라미터와 리턴 타입에 대한 정의를 한 후, 동일한 파라미터와 리턴 타입을 가진 메서드를 서로 호환해서 불러 쓸 수 있는 기능이다.
예를 들면, 아래 RunDelegate 델리게이트는 입력 파라미터가 int 하나이고 리턴 값이 없는 메서드를 가리킨다. RunThis() 메서드와 RunThat()메서드는 모두 int 파라미터 하나에 리턴 값이 없는 메서드이므로, RunDelegate의 함수 형식(prototype)과 맞으므로 이 delegate를 사용할 수 있다. 


예제

using System; 

namespace MySystem{     

	class MyClass{         
    
    	// 1. delegate 선언         
    	private delegate void RunDelegate(int i);         
   
   		private void RunThis(int val){             
        	// 콘솔출력 : 1024             
    		Console.WriteLine("{0}", val);         
       
        }
        
        private void RunThat(int value){             
        	// 콘솔출력 : 0x400             
            Console.WriteLine("0x{0:X}", value);         
        }         
        
        public void Perform(){            
        	
            // 2. delegate 인스턴스 생성             
            RunDelegate run = new RunDelegate(RunThis); 
            
            // 3. delegate 실행             
            run(1024);            
            //run = new RunDelegate(RunThat); 을 줄여서              
            //아래와 같이 쓸 수 있다.             
            run = RunThat;             
            run(1024);         
         }     
     }    
         
         
     class Program{         
     	static void Main(string[] args){   
        
           MyClass mc = new MyClass();             
           mc.Perform();         
        }  
        
     } 
       
}

 

 


 

Delegate를 메서드 파라미터로 전달 

Delegate는 동일한 함수 Prototype을 갖는 메서드를 가리키므로 함수의 포인터를 파라미터로 전달하듯, 다른 함수의 파라미터로 사용될 수 있다. delegate 파라미터를 전달받은 쪽은 이를 자신의 내부 함수를 호출하듯 사용할 수 있다. (C# delegate는 내부적으로 .NET Delegate / MulticastDelegate 클래스를 사용한다. 따라서 이 클래스가 지원하는 속성 (예: .Method - 함수 Prototype을 보여줌)과 메서드 (예: GetInvokcationList())를 모두 사용할 수 있다)

아래 예제는 올림차순으로 비교하는 함수(AscendingCompare) 와 내림차순으로 비교하는 함수(DescendingCompare)를 delegate로 전달하여, 하나의 Sort메서드에서 비교함수에 따라 여러 개의 소트가 가능하다는 것을 보여주는 예이다.
 

예제


class MySort {     
	// 델리게이트 CompareDelegate 선언     
    public delegate int CompareDelegate(int i1, int i2);     
    public static void Sort(int[] arr, CompareDelegate comp){         
    	if (arr.Length < 2) return;         
        Console.WriteLine("함수 Prototype: " + comp.Method);         
        int ret;         
        
        for (int i = 0; i < arr.Length - 1; i++){             
        	for (int j = i+1; j < arr.Length; j++){   
            
            	ret = comp(arr[i], arr[j]);                 
               
                if (ret == -1){                     
                	// 교환                     
                    int tmp = arr[j];                     
                    arr[j] = arr[i];                     
                    arr[i] = tmp;                 
                }             
             }         
          }         
          
          Display(arr);     
     }     
     
     
     static void Display(int[] arr){         
     	foreach (var i in arr) Console.Write(i + " ");         
        Console.WriteLine();     
     } 
} 
        
        
class Program {     
	static void Main(string[] args){         
    	(new Program()).Run();     
    }    
    
    void Run(){        
    	int[] a = { 5, 53, 3, 7, 1 };                  
        
        // 올림차순으로 소트         
        MySort.CompareDelegate compDelegate = AscendingCompare;         
        MySort.Sort(a, compDelegate);         
        
        // 내림차순으로 소트         
        compDelegate = DescendingCompare;         
        MySort.Sort(a, compDelegate);               
     }     
     
     // CompareDelegate 델리게이트와 동일한 Prototype     
     int AscendingCompare(int i1, int i2){         
     	if (i1 == i2) return 0;         
        return (i2 - i1) > 0 ? 1 : -1;     
     }     
     
     // CompareDelegate 델리게이트와 동일한 Prototype     
     int DescendingCompare(int i1, int i2){         
     	if (i1 == i2) return 0;         
        return (i1 - i2) > 0 ? 1 : -1;     
     } 
     
 }

 

 

 

=======================

=======================

=======================

 

 

 

출처: http://www.csharpstudy.com/csharp/CSharp-delegate-concept.aspx

 

 

C# delegate 쉽게 이해하기 

C# delegate는 약간 낯선 개념이고 경우에 따라 어렵게 느껴질 수 있다. 여기서 그 기초 개념이 무엇인지 자세히 집어보도록 하자.

다음과 같은 하나의 함수가 있다고 가정하자.
(주: 여기서의 함수는 개념적으로 메서드와 동일한 의미로 가정)

 

 

이 함수는 정수 하나를 파라미터로 받아들인다. 이 함수를 호출하기 위해서는 아래와 같이 정수를 메서드 파라미터로 넘기면 된다.

 

 

좀 더 복잡한 경우로서 다음과 같이 클래스 객체를 넘기는 경우를 생각해 볼 수 있다.

 

 

이 경우 MyClass는 위의 int와 같은 Built-in 타입이 아니므로 개발자가 그 타입을 어딘가에서 정의해 주어여야 한다 (예를 들어 아래와 같이).

 

 

이 함수(RunB)를 호출하기 위해서는 아래와 같이 클래스 인스턴스를 만들어 이를 메서드 파라미터로 넘기면 된다.

 

 

그런데 위의 2가지 케이스를 자세히 보면, 파라미터로서 정수 혹은 객체 즉 어떤 "데이타"를 메서드에 보내고 있는 것을 알 수 있다.

그러면, 이러한 통상적인 개념의 물리적 "데이타" 말고, 어떤 "메서드" 자체를 함수(메서드)의 파라미터로 전달할 수 있을까?

(주: 사실 "추상적 개념으로" 클래스 객체는 데이타(필드)와 행위(메서드)를 함께 포함하고 있는 것이고, 이를 메서드 파라미터로 보낼 수 있다는 것은, 클래스의 일부인 메서드만을 보낼 수 있음을 (보낼 수 있게 만들 수 있음을) 의미하기도 한다.)

Delegate는 이렇게 메서드를 다른 메서드로 전달할 수 있도록 하기 위해 만들어 졌다. 아래 그림에서 보이듯이, 숫자 혹은 객체를 메서드의 파라미터로써 전달할 수 있듯이, 메서드 또한 파라미터로서 전달할 수 있다. (주: 복수 개의 메서드들도 전달 가능) Delegate는 메서드의 입력 파라미터로 피호출자에게 전달될 수 있을 뿐만 아니라, 또한 메서드의 리턴값으로 호출자에게 전달 수도 있다.

예를 들어, 다음과 같은 함수를 가정해 보자. 여기서 MyDelegate가 델리게이트 타입이라고 가정하면, 이 함수는 다른 어떤 메서드를 Run() 메서드에 전달하고 있는 것이다.

 

 

그러면 델리게이트 타입 MyDelegate은 어떻게 정의되는가? 위의 클래스 예제(MyClass)의 경우 C# 키워드 class 를 사용하여 해당 클래스를 정의하였는데, 델리케이트 타입을 정의하기 위해선 특별한 C# 키워드 delegate 를 사용한다.

 

 

델리케이트 정의에서 중요한 것은 입력 파리미터들과 리턴 타입이다. 만약 어떤 메서드가 이러한 델리게이트 메서드 원형(Prototype)과 일치한다면, 즉 입력 파리미터 타입 및 갯수, 리턴 타입이 동일하다면 그 메서드는 해당 델리게이트에서 사용될 수 있다.

델리케이트 정의는 마치 하나의 함수/메서드를 정의하는 Prototype 선언식처럼 보이는데, 사실 내부적으로 이 선언식은 컴파일러에 의해 특별한 클래스로 변환된다. 
(주: C# 컴파일러는 위의 delegate 정의를 읽어, System.MulticastDelegate 클래스로부터 파생된 MyDelegate 클래스를 생성하게 된다. 따라서 delegate는 메서드를 전달하기 위해 메서드 메타정보를 내부에 갖고 있는 특별한 종류의 Wrapper 클래스라 볼 수 있다. 그러면, 델리게이트 생성시 C# delegate 키워드 말고 직접 System.MulticastDelegate 클래스로부터 파생된 클래스를 만들 수 있을까? 이는 불가능하다. System.MulticastDelegate 클래스는 특별한 .NET 클래스로 Base클래스로 사용될 수 없다.)
이렇게 C# delegate 식을 클래스가 아닌 함수 선억식처럼 정의하게 한 것은 내부의 복잡한 설계를 숨기고 '메서드를 전달하는 본연의 의도'를 더 직관적으로 표현하기 위한 것으로 볼 수 있다.

델리케이트가 이렇게 정의된 후에는 클래스 객체를 생성한 것과 비슷한 방식으로 new를 써서 델리케이트 객체를 생성한다.
(주: delegate는 결국 클래스이므로 클래스 객체 생성과 같은 방식을 사용한다)

델리케이트를 다른 메서드에 전달하는 방식은 델리케이트 객체를 메서드 호출 파라미터에 넣으면 된다. 이는 메서드를, 좀 더 정확히는 그 메서드 정보를 갖는 Wrapper 클래스의 객체를, 파라미터로 전달하는 것이 된다.

 

 

전달된 델리게이트로부터 실제 메서드를 호출하는 것은 어떻게 하는가? 이는 델리게이트 객체의 .Invoke() 메서드나 .BeginInvoke() 메서드를 써서 호출한다. 예를 들어, m 이라는 델리게이트 객체를 전달 받았을 경우, 아래와 같이 Invoke() 메서드를 호출한다. 만약 메서드에 입력파라미터가 있을 경우, 이를 Invoke() 안에 추가한다.

 

 

또 다른 메서드 호출방법으로 C# 프로그래머들이 더 애용하는 방식은, .Invoke 메서드명을 생략하고 다음과 같이 직접 함수처럼 사용하는 방법이다. 이 방식은 마치 메서드를 직접 호출하는 느낌을 주므로 더 직관적이다.

 

 

참고로, 아래 예제는 위의 설명을 종합한 간단한 delegate 샘플이다.

예제

class Program {     
	static void Main(string[] args){         
    	new Program().Test();     
    }     
    
    // 델리게이트 정의     
    delegate int MyDelegate(string s);     
    
    void Test(){         
    	//델리게이트 객체 생성         
    	MyDelegate m = new MyDelegate(StringToInt);     
    
   		//델리게이트 객체를 메서드로 전달        
    	Run(m);     
     }    
     
     // 델리게이트 대상이 되는 어떤 메서드     
     int StringToInt(string s){         
     	return int.Parse(s);     
     }     
     
     // 델리게이트를 전달 받는 메서드     
     void Run(MyDelegate m){         
     	// 델리게이트로부터 메서드 실행         
        int i = m("123");         
        Console.WriteLine(i);     
     } 
}

 

 

=======================

=======================

=======================

 

 

반응형