Delegate의 활용
일련의 사건이 발생했다는 사실을 다른 객체하게 전달할 때 앞서 배운 Delegate를 활용할 수 있는데, 이때 일련의 사건을 Event로 표현하여 키워드로써 delegate의 일부 기능을 제한하는 것이 가능하다.
public class Player
{
public int hp = 100;
public Action OnDied;
public void Die()
{
Console.WriteLine("플레이어가 쓰러집니다.");
OnDied?.Invoke();
}
}
public class Monster
{
public void Stop()
{
Console.WriteLine("몬스터가 더이상 플레이어를 쫓지 않습니다.");
}
}
static void Main(string[] args)
{
Player player = new Player();
Monster monster1 = new Monster();
Monster monster2 = new Monster();
player.OnDied += monster1.Stop;
player.OnDied = monster2.Stop;
}
앞서 말했듯 delegate로 사건이 일어났다는 사실을 다른 객체로 넘겨줄 수 있으므로, 전달할 상황을 delegate로 선언하면 될 것이다. 위처럼 OnDied
라는 직관적인 코드로 '죽을 때' 라는 상황을 가정하여 delegate로 선언해보자. '죽을 때'의 상황에서 플레이어는 Die
죽을 것이고, 몬스터는 Stop
쫓는 것을 멈출 것이다. 하지만 OnDied
는 '플레이어'의 상태이므로 이를 다른 객체 '몬스터'에게 이를 전달하기 위해서는 Delegate OnDied
에 함수를 추가하는 행위가 필요할 것이다. 따라서 메인 메서드에서 함수를 추가해줌으로써 몬스터에게 해당 상황이 전달될 것이고, 해당 상황이 되었을 때 몬스터의 Stop
함수가 구동되는 것이다.
Event
public class Player
{
public Action OnDied;
public void Die()
{
OnDied?.Invoke();
}
}
위의 코드를 다시 한 번 살펴보자. Action Ondied
를 Public
으로 선언했기에, 외부에서 해당 상황에 대해 초기화를 하거나 상황이 발생했다고 호출하는 것이 가능해진다. 하지만 보통 해당 사항은 개발자가 원하는 것이 아니므로 event
키워드와 함께 선언하므로써 delegate의 기능을 제한할 수 있다.
```cs
public class Player
{
public event Action OnDied;
public void Die()
{
OnDied?.Invoke();
}
}
static void Main(string[] args)
{
player.OnDied = bossMonster.Stop; // 구문 오류
player.OnDied(); // 구문 오류
}
이렇게 event
키워드와 함께 선언을 하면, 메인 메서드에서 delegate를 호출하거나, 대입연산자를 통해 초기화하는 것이 모두 에러가 걸리게 된다.
델리게이트에 할당하고자 하는 함수를 소스코드 구문에서 생성하여 전달할 수 있다.
static void Main(string[] args)
{
Func<int, int, int> pow = delegate (int n, int x)
{
int result = 1;
for (int i = 0; i < x; i++)
{
result *= n;
}
return result;
};
}
위처럼 델리게이트 할당과 동시에 함수를 생성하는 것이 무명 메서드이다. 할당하기 위한 함수가 간단하고, 1회성으로 사용될 경우에 효율적으로 사용할 수 있다. 물론 여기서 더 간단하게 작성하는게 가능한데, 이를 람다식이라고 한다.
Lambda식
static void Main(string[] args)
{
Func<int, int, int> pow = (n, x) =>
{
int result = 1;
for (int i = 0; i < x; i++)
{
result *= n;
}
return result;
};
무명 메소드 선언에서 delegate와 매개변수 자료형을 생략한 것이 람다식이다. 더 간단하게 작성하는 것이 가능하며, delegate의 확장에도 많이 이용된다.
public class Player
{
public Action<int> OnTakeDamaged;
}
public class SFX
{
public void HitSound()
{
Console.WriteLine("피격음 사운드를 재생합니다.");
}
}
static void Main(string[] args)
{
Player player = new Player();
SFX sfx = new SFX();
player.OnTakeDamaged += sfx.HitSound; // 구문 오류
player.OnTakeDamaged += (damage) => { sfx.HitSound(); };
}
위 코드는 delegate OnTakeDamaged
에 HitSound
를 추가하려했지만 매개변수가 달라 구문 오류가 나는 상황이다. 이때 람다식을 사용하면 맨 아래처럼 추가하는 것이 가능해진다. 이를 해석하면 damage
매개변수를 넣어주지만 쓰지 않고 HitSound
를 사용하겠다는 의미이다.
static void Main(string[] args)
{
int[] array = { 1, 2, 3, 4, 5, 6, 7 };
int[] result1 = Array.FindAll(array, IsOdd);
int[] result2 = Array.FindAll(array, value => value % 2 != 0 );
}
public static bool IsOdd(int value)
{
return value % 2 != 0;
}
위처럼 bool 자료형 매개변수에 bool 반환형 함수를 넣는 상황이라고 가정해보자. 물론 IsOdd
처럼 함수를 정의하고 호출하는 것도 방법이겠지만, 구문 내에서 선언하는 것도 가능하다. result2
처럼 람다식을 활용하는 것이 그 예시이며, 이를 통해 조금 더 간결한 코드 작성이 가능하다.