Недавно я был немного странным, с выражением лямбда и переменными захватами. Код был приложением WPF/MVVM с использованием .NET 4.5 (VS2012). Я использовал разные конструкторы моей модели для установки обратного вызова для RelayCommand
(эта команда будет привязана к элементу меню в моем представлении)
В сущности, у меня был следующий код:
public class MyViewModel : ViewModelBase
{
public MyViewModel(Action menuCallback)
{
MyCommand = new RelayCommand(menuCallback);
}
public MyViewModel(Func<ViewModelBase> viewModelCreator)
// I also tried calling the other constructor, but the result was the same
// : this(() => SetMainContent(viewModelCreator())
{
Action action = () => SetMainContent(viewModelCreator());
MyCommand = new RelayCommand(action);
}
public ICommand MyCommand { get; private set; }
}
а затем создайте экземпляры выше, используя:
// From some other viewmodel code:
new MyViewModel(() => new SomeViewModel());
new MyViewModel(() => new SomeOtherViewModel());
Затем они были привязаны к меню WPF - каждый элемент меню имел экземпляр MyViewModel в качестве своего контекста данных. Странно, что меню только работало. Независимо от того, какой из элементов я попытался, он назовет соответствующий Func<ViewModelBase>
- но только один раз. Если я попытался снова выбрать другой пункт меню или даже тот же элемент, он просто не сработает. Ничего не вызвало и не выводило на выходе VS отладки о каких-либо ошибках.
Мне известно о проблемах с захватами переменных в циклах, поэтому я предположил, что эта проблема была связана, поэтому изменила мою виртуальную машину на:
public class MyViewModel : ViewModelBase
{
public MyViewModel(Action buttonCallback)
{
MyCommand = new RelayCommand(buttonCallback);
}
private Func<ViewModelBase> _creator;
public MyViewModel(Func<ViewModelBase> viewModelCreator)
{
// Store the Func<> to a field and use that in the Action lambda
_creator = viewModelCreator;
var action = () => SetMainContent(_creator());
MyCommand = new RelayCommand(action);
}
public ICommand MyCommand { get; private set; }
}
и называется так же. Теперь все работает так, как должно.
Просто для удовольствия, я также работал вокруг всего конструктора Func<ViewModelBase>
, создав соответствующий Action
вне конструктора MyViewModel
:
// This code also works, even without the _creator field in MyViewModel
new MyViewModel(() => SetMainContent(new SomeViewModel()));
new MyViewModel(() => SetMainContent(new SomeOtherViewModel()));
Поэтому мне удалось заставить его работать, но мне все еще интересно, почему он работает так. Почему компилятор не правильно захватил Func<ViewModelBase>
в конструкторе?