Open-Closed Principle

[C#] Delegate 사용법 본문

Programming/C#

[C#] Delegate 사용법

대박플머 2014. 6. 12. 18:18

오늘 알아볼 녀석을 Delegate이다. 

입문자의 경우는 Delegate를 사용에 대해 의문의 갖는다. 아주 많이...

하지만 C#에서 자랑하는 기능이니, 알아두고 유용하게 쓸만한 곳을 찾아보는 것도 좋겠다. 


Delegate는 특정 매개 변수 및 반환 형식이 있는 메서드에 대한 참조를 나타내는 형식이다.

Delegate를 인스턴스화하면 메개 변수 및 반환 형식이 같은 모든 메서드 형식에 연결할 수 있고,인스턴스를 통해 메서드를 호출할 수 있다.

Delegate 속성

1. Delegate는 C++의 함수 포인터와 유사하지만 안전하다.

2. Delegate를 통해 메서드를 매개 변수로 전달할 수 있다.

3. Delegate를 사용하여 콜백 메서드를 정의할 수 있다.

4. 여러 Delegate를 연결할 수 있다.

5. 메서드와 Delegate 형식이 정확히 일치할 필요는 없습니다. 

6. C# 버전 2.0에는 별도로 정의된 메서드 대신 코드 블록을 매개 변수로 전달할 수 있도록 하는 무명 메서드라는 개념이 도입되었습니다. C# 3.0에는 인라인 코드 블록을 더 간단하게 작성할 수 있는 람다 식이 도입되었습니다. 특정 컨텍스트에서는 무명 메서드와 람다 식 모두 대리자 형식으로 컴파일됩니다.  이 두 기능을 익명 함수라고 합니다.


※ Delegate의 사용에대해 알아보자. 

Delegate는 메서드를 안전하게 캡슐화하는 형식으로, C 및 C++의 함수 포인터와 유사하다.

하지만 C의 함수 포인터와 달리 Delegate는 객체 지향적이고 형식 안전성을 제공하며 보안상 안전하다.

Delegate의 형식은 Delegate의 이름으로 정의된다.

반환형이 없고 String 형식의 매개변수를 받는 Delegate를 만들어보자.

1
2
3
 
public delegate void MyDelegate(string msg);
 

어렵지 않게 Delegate는 선언할 수 있다. 


Delegate 객체는 일반적으로 Delegate가 래핑할 메서드의 이름을 제공하거나 무명 메서드를 사용하는 방법으로 만들어진다. Delegate가 인스턴스화되면 Delgate에 대한 메서드 호출이 해당 메서드로 전달된다. 호출자가 Delegate에 전달한 매개 변수가 메서드에 전달되고, 메서드에 반환 값이 있으면 Delegate가 해당 값을 호출자에게 반환한다. 이 과정을 Delegate 호출이라고 한다. 사용자는 인스턴스화된 Delegate를 래핑된 메서드 자체인 것처럼 호출할 수 있습니다. 

Delegate에 메소드를 래핑하는 과정을 보겠다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System;
 
public class Program{
    public delegate void MyDelegate(string msg);
 
    static void HelloPrintStatic(string msg){
        Console.WriteLine(msg + " Hello World!");
    }
 
    public void HelloPrintMember(string msg){
        Console.WriteLine(msg + " Hello World!")
    }
    public static void Main(){
        MyDelegate delegateHelloStatic = Program.HelloPrintStatic;
        delegateHelloStatic("Static");
        Program pro = new Program();
        MyDelegate delegateHelloMember = pro.HelloPrintMember;
        delegateHelloMember("Member");
    }
}

위의 방법은 일반적인 Delegate를 래핑하는 방법이고

무명 메소드 또한 래핑할 수 있다. 
1
2
3
4
5
6
7
8
9
10
11
12
13
using System;
 
public class Program{
    public delegate void MyDelegate(string msg);
 
    public static void Main(){
        MyDelegate myDelegate = delegate(string msg){
            Console.WriteLine(msg + " Hello World!");
        }
 
        myDelegate("anonymous Method");
          }
}

래핑은 이정도 이다. 한가지 더 말한다면 람다표현식으로도 할 수 있다. 
하지만 C# 2.0에서는 제공하지 않고, C# 3.0부터 제공한다는 사실을 알아야 할 것이다. 

다음으로 초반부에 말했던 반환형식과 매개변수가 같아야 한다고 이야기 했었다. 
하지만 예외적인 경우가 있다. 정확이 이야기 하면 당연한 것일 수도 있다. 
우선 예제를 보고 이야기 하자. 

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 
using System;
 
public class Animal {
    private string name;
    private int age;
 
    public string Name{
        get {return this.name;}
        set {this.name = value;}
    }
 
    public int Age{
        get {return this.age;}
        set {this.age = value;}
    }
    public Animal(string name, int age){
        this.Name = name;
        this.Age = age;
    }
    public virtual void Hello(){    // A
        Console.WriteLine("Hello Animal! " + this.Name);
    }
}
 
public class Dog : Animal{
    private string nickName;
    public string NickName{
        get {return this.nickName;}
        set {this.nickName = value;}
    }
    public Dog(string name, int age, string nickName) : base(name, age){
        this.NickName = nickName;
    }
    public override void Hello(){ // B
        Console.WriteLine("Hello Dog " + this.Name);
    }
 
}
 
public class Program{
    public delegate Animal AnimalHandler(string name);
 
    public static void Main(){
        AnimalHandler handler = delegate(String name)
        {
            Dog d = new Dog(name, 12, name +" NickName");
 
            return d; // C
        };
 
        Animal ani = handler("Juu"); //D
 
        ani.Hello(); 
    }
}

이 예제를 보면 알겠지만 부모 객체를 통해 자식을 호출하기 위함이다. 
예제를 복사해서 돌려보면 알겠지만 "Hello Dog Juu"가 나온다. 

중요한 부분은 바로 "C"부분이다. 반환을 분명 Dog로 하고 있는데도 애러가 발생하지 않는다. 
바로 마지막 Delegate 사용법은 이것이다. 자식을 반환 형식이나 매개변수로 사용해도 아무 문제가 없다는 것이다. 

그리고 이제 마지막으로 같은 형식의 메소드를 묶어서 사용하는 것이다. 
이것은 예제만 보면 알 수 있을 것이다. 

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
32
33
34
 
using System;
 
public class Program {
    public delegate void MyDelegate(string msg);
 
    static int iCount = 1;
 
    public static void CounterStatic(string msg) {
        Console.WriteLine("CounterStatic "+msg+" : " + iCount);
        iCount++;
    }
 
    public void CounterMember(string msg) {
        Console.WriteLine("CounterMember " + msg + " : " + iCount);
        iCount++;
    }
 
    public static void Main() {
        Program pro = new Program();
 
        MyDelegate handler1 = Program.CounterStatic;
        handler1 += pro.CounterMember;
 
        handler1("Called");
 
        Console.WriteLine();
        iCount = 1;
        MyDelegate handler2 = pro.CounterMember;
        handler2 += Program.CounterStatic;
 
        handler2("Called");
    }
}

예제를 보면 알겠지만 순서를 정해서 같은 형식의 메소드를 집어 넣으면 아주 쉽게 순차적으로 부러준다. 


이렇게 Delegate의 사용법은 끝....