Here are the references in this example:
- View:
- Selector
- ViewModel:
- ICommand
- Selector:
- Anonymous delegate
- ICommand
- Anonymous delegate:
- Selector
- ICommand
This means the view and view-model can be garbage collected, leaving the Selector
and ICommand
alive.
The garbage collector is capable of dealing with circular references; so even though the Selector
references the delegate, and the delegate references the Selector
these can still be garbage collected.
The ICommand
however, will be kept alive as long as this anonymous delegate is kept alive, which is determined solely by the lifetime of the Selector
instance. As long as the Selector
is being garbage collected, the delegate and ICommand
will eventually be garbage collected too.
So in simple situations, no, your code does not cause a leak.
There is however a situation where your code does leak handlers, I am assuming your view-model has a property like so:
public ICommand OnSelectionChanged
{
get { return _onSelectionChanged; }
private set
{
_onSelectionChanged = value;
RaisePropertyChanged("OnSelectionChanged");
}
}
Which is then bound in the view, if you change the value of this OnSelectionChanged
command, your attached property will leak event handlers, as you never unsubscribe the delegate which executes the old command.
So rather than just one command being executed, all of the previous values of this property will be executed.
I would go for an implementation more like the following:
private static void OnSelectionCommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var selector = d as Selector;
if (selector != null)
{
var oldCommand = e.OldValue as ICommand;
var newCommand = e.NewValue as ICommand;
if(oldCommand == null && newCommand != null)
{
selector.SelectionChanged += OnSelectionChanged;
}
else
{
selector.SelectionChanged -= OnSelectionChanged;
}
}
}
private static void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selector = (Selector)sender;
var command = GetSelectionCommand(selector);
if(command != null)
{
command.Execute(selector.SelectedItem);
}
}