【问题标题】:Why do I have to capture the lambda to a field variable in call to constructor为什么我必须在调用构造函数时将 lambda 捕获到字段变量
【发布时间】:2012-09-25 11:41:31
【问题描述】:

我最近被 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&lt;ViewModelBase&gt; - 但只有一次。如果我尝试再次选择另一个菜单项,甚至是同一个项目,它根本不起作用。 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; }
}

并以同样的方式称呼它。现在一切正常。

只是为了好玩,我还通过在 MyViewModel 构造函数之外创建适当的 Action 来解决整个 Func&lt;ViewModelBase&gt; 构造函数:

// This code also works, even without the _creator field in MyViewModel
new MyViewModel(() => SetMainContent(new SomeViewModel()));
new MyViewModel(() => SetMainContent(new SomeOtherViewModel()));

所以我设法让它工作,但我仍然很好奇它为什么会这样工作。为什么编译器没有正确捕获构造函数中的Func&lt;ViewModelBase&gt;

【问题讨论】:

  • 您是否查看过为两种方法生成的 IL 的差异?它可能会给出一些提示。
  • 如果 SetMainContentViewModelBase 类中,您如何在最后一个有效的代码示例中的 lambda 中调用它?
  • SetMainContent 使用消息(来自 MVVMLight)将视图模型实例发送到主窗口的视图模型。然后它将其分配给在 UI 中呈现的 Content 属性。我会尝试提出一个更完整的代码示例来展示这个问题

标签: c# wpf lambda closures


【解决方案1】:

我猜下面的代码也可以工作

public MyViewModel(Func<ViewModelBase> viewModelCreator)
{
    var action = () => { creator = viewModelCreator; SetMainContent(creator()); };
    MyCommand = new RelayCommand(action);
}

如果是这样,那么第一种方法不起作用的原因是您实际上并没有关闭 viewModelCreator 变量,而是关闭了调用它的结果。

我仍在使用 LINQPad 中的代码,但实际上我遇到的问题似乎与您不同。也许这是RelayCommand 特有的东西。你能发布更多你的代码吗?

【讨论】:

  • Hmm.. 尝试使用我自己问题中的代码在另一台计算机(使用 VS2010)上重现它,但我没有看到它.. 我需要回到我的原始代码,看看是什么不同的是(我的问题代码被简化了——可能是我简化太多了,把过程中的问题去掉了)
猜你喜欢
  • 2020-11-15
  • 1970-01-01
  • 2022-01-17
  • 2011-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-14
  • 1970-01-01
相关资源
最近更新 更多