◆ ICommand의 기능과 순환참조 메모리 누수를 위한 WeakReference 의 활용한 클래스 작성.
아래의 CommandManagerHelper 클래스는 메모리 누수를 방지하는데 도움이 되는 메서드입니다.
일반적인 이벤트 처리기를 사용할 경우, 강한연결로 메모리의 누수가 발생할 수 있습니다.
WeakReference는 순환참조 및 느슨한 연결로 이러한 누수들을 예방할 수 있습니다.
internal class CommandManagerHelper
{
internal static void CallWeakReferenceHandlers(List<WeakReference> handlers)
{
if (handlers != null)
{
// 핸들러를 호출하기 전에 핸들러의 스냅샷을 찍습니다.
// 배열을 읽는 동안 배열이 수정될 수 있습니다.
EventHandler[] callees = new EventHandler[handlers.Count];
int count = 0;
for (int i = handlers.Count - 1; i >= 0; i--)
{
WeakReference reference = handlers[i];
EventHandler handler = reference.Target as EventHandler;
if (handler == null)
{
// 수집된 이전 핸드러를 정리합니다.
handlers.RemoveAt(i);
}
else
{
callees[count] = handler;
count++;
}
}
// 스냅샷을 생성한 핸들러를 호출합니다.
for (int i = 0; i < count; i++)
{
EventHandler handler = callees[i];
handler(null, EventArgs.Empty);
}
}
}
internal static void AddHandlersToRequerySuggested(List<WeakReference> handlers)
{
if (handlers != null)
{
foreach (WeakReference handlerRef in handlers)
{
EventHandler handler = handlerRef.Target as EventHandler;
if (handler != null)
{
CommandManager.RequerySuggested += handler;
}
}
}
}
internal static void RemoveHandlersFromRequerySuggested(List<WeakReference> handlers)
{
if (handlers != null)
{
foreach (WeakReference handlerRef in handlers)
{
EventHandler handler = handlerRef.Target as EventHandler;
if (handler != null)
{
CommandManager.RequerySuggested -= handler;
}
}
}
}
internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler)
{
AddWeakReferenceHandler(ref handlers, handler, -1);
}
internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler, int defaultListSize)
{
if (handlers == null)
{
handlers = (defaultListSize > 0 ? new List<WeakReference>(defaultListSize) : new List<WeakReference>());
}
handlers.Add(new WeakReference(handler));
}
internal static void RemoveWeakReferenceHandler(List<WeakReference> handlers, EventHandler handler)
{
if (handlers != null)
{
for (int i = handlers.Count - 1; i >= 0; i--)
{
WeakReference reference = handlers[i];
EventHandler existingHandler = reference.Target as EventHandler;
if ((existingHandler == null) || (existingHandler == handler))
{
// 수집된 이전 처리기를 정리합니다.
// 제거할 핸드러에 추가합니다.
handlers.RemoveAt(i);
}
}
}
}
}
◆ 매개변수를 갖는 DelegateCommand<T>
public class DelegateCommand<T> : ICommand
{
#region Constructors
/// <summary>
/// 생성자입니다.
/// </summary>
public DelegateCommand(Action<T> executeMethod)
: this(executeMethod, null, false)
{
}
/// <summary>
/// 생성자입니다.
/// </summary>
public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
: this(executeMethod, canExecuteMethod, false)
{
}
/// <summary>
/// 생성자입니다.
/// </summary>
public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
{
if (executeMethod == null)
{
throw new ArgumentNullException("executeMethod");
}
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
_isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
}
#endregion
#region Public Methods
/// <summary>
/// 명령을 실행할 수 있는지 확인합니다.
/// </summary>
public bool CanExecute(T parameter)
{
if (_canExecuteMethod != null)
{
return _canExecuteMethod(parameter);
}
return true;
}
/// <summary>
/// 명령 실행함수입니다.
/// </summary>
public void Execute(T parameter)
{
if (_executeMethod != null)
{
_executeMethod(parameter);
}
}
/// <summary>
/// CanExecuteChaged 이벤트를 발생시킵니다.
/// </summary>
public void RaiseCanExecuteChanged()
{
OnCanExecuteChanged();
}
/// <summary>
/// CanExecuteChanged 이벤트를 발생시키는 Virtual 메서드
/// </summary>
protected virtual void OnCanExecuteChanged()
{
CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
}
/// <summary>
/// CommandManager의 자동 재쿼리를 활성화 또는 비활성화하는 속성
/// </summary>
public bool IsAutomaticRequeryDisabled
{
get
{
return _isAutomaticRequeryDisabled;
}
set
{
if (_isAutomaticRequeryDisabled != value)
{
if (value)
{
CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
}
else
{
CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
}
_isAutomaticRequeryDisabled = value;
}
}
}
#endregion
#region ICommand Members
/// <summary>
/// ICommand.CanExecuteChanged 구현부입니다.
/// </summary>
public event EventHandler CanExecuteChanged
{
add
{
if (!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested += value;
}
CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
}
remove
{
if (!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested -= value;
}
CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
}
}
bool ICommand.CanExecute(object parameter)
{
if (parameter == null &&
typeof(T).IsValueType)
{
return (_canExecuteMethod == null);
}
return CanExecute((T)parameter);
}
void ICommand.Execute(object parameter)
{
Execute((T)parameter);
}
#endregion
#region Data
private readonly Action<T> _executeMethod = null;
private readonly Func<T, bool> _canExecuteMethod = null;
private bool _isAutomaticRequeryDisabled = false;
private List<WeakReference> _canExecuteChangedHandlers;
#endregion
}