처음 WPF 프로젝트를 다룰 때 MVVM 적용하는데 필요한 도구 중에 MVVMLight을 매우 유용하게 사용했습니다.
그 중에 Messenger 라는 클래스가 있는데 특정 Type을 기억해두고 해당 클래스를 Send와 Register를 통해 ViewModel간의 data 전달하는데 매우 유용하게 썼었던지라 해당 Messenger를 다른프로젝트에서 따로 아래와 같이 구현해보았습니다.
정확하게 동일하게 작동하는지는 알 수 없지만 직접 고민해서 작성했던 코드이고 현재까지 사용하는데 있어서 의도대로 작동하고있고 별다른 이슈도 없어서 나름 기록하고자 포스트합니다.
public class Messenger
{
private readonly Dictionary<Type, Action<object>> _actions;
private readonly List<Tuple<Delegate, Action<object>>> _actionTuples;
private static Messenger _instance;
private static readonly object _lock = new object();
//Singletone Instance
public static Messenger Instance { get { lock (_lock) { return _instance ??= new Messenger(); } } }
//Singletone 외부에서 생성이 안되도록
private Messenger()
{
//실제로 Subscribe된 Delegate를 실행하는 곳.
_actions = new Dictionary<Type, Action<object>>();
//Subscribe한 Delegate를 Action<object> 형식으로 만들어서 가지고 있음.
_actionTuples = new List<Tuple<Delegate, Action<object>>>();
}
public void Subscribe<T>(Action<T> action)
{
Action<object> input = null;
input = _actionTuples.Find(x => x.Item1.Equals(action))?.Item2;
if (input == null)
{
//Action<T>를 Action<object> 형태로 변형해서 List에 추가.
input = new Action<object>(o => action((T)o));
_actionTuples.Add(new Tuple<Delegate, Action<object>>(action, input));
}
else
{
return;
}
//Action<T>가 아닌 Action<object>를 Dctionary에 넣어준다.
if (!_actions.ContainsKey(typeof(T)))
{
_actions.Add(typeof(T), input);
}
else
{
_actions[typeof(T)] += input;
}
}
public void Unsubscribe<T>(Action<T> action)
{
//Action<T>로 Action<object>를 찾고 Dictionary에서 제거
Action<object> input = null;
input = _actionTuples.Find(x => x.Item1.Equals(action))?.Item2;
if (input == null)
{
return;
}
else
{
_actions[typeof(T)] -= input;
}
}
public void Publish<T>(T obj)
{
if (!_actions.ContainsKey(typeof(T)))
{
return;
}
_actions[typeof(T)]?.Invoke(obj);
}
}
Messenger 클래스 자체는 싱글톤으로 만들었는데 다른 Thread에서 접근할 소지가 있어서 따로 lock 키워드를 통해 동기화를 하려고 하였습니다.
Type에 따른 delegate를 저장할때 Action<T>를 지정하고 싶으나 Dictionary에 제네릭 형태로는 저장할 수가 없어서 모든 인수를 받을수 있는 object로 지정하였습니다. 때문에 생기는 문제가 있는데 2가지가 있는데
1. Dictionary에 Action<T> 형태로 추가가 안됩니다.
Subscribe를 할 때는 Action<T>로 받았으니 그대로는 Dictionary에 Add할 수가 없다는 것입니다. 그래서 Action<T>를 Action<object>로 한번 형태를 변형해서 Dictionary에 추가해주게 되었습니다.
2. Subscribe 했던 객체가 유지가 안됩니다.
Subscribe 했던 Action<T>를 Action<object>로 변형해서 Dictionary에 추가를 해주었으니 원래 추가하려고 했던 Action<T>는 쓰이지 않게 됩니다. 여기서 생기는 문제는 Unsubcribe를 할 수가 없다는 것입니다. ViewModel에서 같은 함수를 Subscribe하고 다시 Unsubscribe 하는데 애초에 의도했던 함수는 안쓰고 새로 생성된 Action<object> 객체를 사용하고 있었으니 해제를 하려고해도 찾을수가 없는 것이죠 그래서 해당 문제를 해결하기 위해 List로 따로 관리해주고 있습니다.
현재는 해당 코드를 통해 의도한대로 잘 사용은 하고 있으나 더 좋은 코드가 또 있을지도 모른다는 생각은 드는데 더이상 감당은 안되네요. 좀 더 고민은 해봐야할 것 같습니다.
'SW개발 > c#' 카테고리의 다른 글
[C#] SubClass list 생성 (0) | 2023.09.01 |
---|---|
[WPF] CallerMemberName (Attribute) - Property 구현 (0) | 2023.02.20 |
[WPF]WndProc - 윈도우메시지 처리 적용 (0) | 2023.02.14 |
[WPF] 모든 에러 발생시 이벤트 발생시키기 (0) | 2023.02.14 |
[C#] Logger (0) | 2023.01.31 |