我最近被 lambda 表达式和变量捕获的奇怪事情所困扰。该代码是使用 .NET 4.5 (VS2012) 的 WPF/MVVM 应用程序。我正在使用我的视图模型的不同构造函数来设置回调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's code:
new MyViewModel(() => new SomeViewModel());
new MyViewModel(() => new SomeOtherViewModel());
然后将这些绑定到 WPF 菜单 - 每个菜单项都有一个 MyViewModel 实例作为其数据上下文。奇怪的是菜单只工作一次。无论我尝试了哪些项目,它都会调用适当的Func<ViewModelBase>
- 但只有一次。如果我尝试再次选择另一个菜单项甚至相同的项目,它根本就不起作用。VS 调试输出中没有任何关于任何错误的调用和输出。
我知道循环中变量捕获的问题,所以我猜测这个问题是相关的,所以将我的 VM 更改为:
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>
构造函数中的 ?