c# – 来自lambda的RelayCommand和构造函数参数

如果在XAML文件中,我将Button从以下类绑定到“Command”,则单击Button不会导致执行DoIt:

class Thing()
{
  public Thing(Foo p1)
  {
    Command = new RelayCommand(() => DoIt(p1));
  }

  private DoIt(Foo p)
  {
    p.DoSomething();
  }

  public ICommand Command { get; private set; }
}

但是,如果我从p1初始化一个字段并将该字段作为参数传递给lambda中的方法调用,它确实有效:

class Thing()
{
  private Foo field;
  public Thing(Foo p1)
  {
    field = p1;
    Command = new RelayCommand(() => DoIt(field));
  }

  private DoIt(Foo p)
  {
    p.DoSomething();
  }

  public ICommand Command { get; private set; }
}

前者为什么会失败,但后者是按预期工作的?

可能相关:How do closures work behind the scenes? (C#)

编辑:澄清一下,以下内容对我也有用.但是,我仍然想知道为什么第二个例子做了我的预期,但第一个例子没有.

class Thing()
{
  private Foo field;
  public Thing(Foo p1)
  {
    field = p1;
    Command = new RelayCommand(DoIt);
    //Command = new RelayCommand(() => DoIt()); Equivalent?
  }

  private DoIt()
  {
    field.DoSomething();
  }

  public ICommand Command { get; private set; }
}

最佳答案 这是一个老问题,但我最近偶然发现了这个话题,值得回答.

这种奇怪行为的原因来自RelayCommand的MVVM Light实现. execute和canexecute处理程序存储为WeakAction _execute和WeakFunc< bool> _canExecute在relay命令中. WeakAction尝试在UI由于某种原因仍然引用该命令时允许GC清理视图模型.

跳过一些细节,底线是:指定一个viewmodel方法,因为处理程序工作得很好,因为只要viewmodel保持活动状态,WeakAction就会保持活动状态.对于动态创建的Action,情况就不同了.如果对该操作的唯一引用在RelayCommand中,则只存在弱引用,GC可以随时收集操作,将整个RelayCommand变为死砖.

好的,详细的时间. WeakAction的实现不是盲目地存储对动作的弱引用 – 这将导致许多消失的引用.而是存储弱Delegate.Target引用和Delegate.MethodInfo的组合.对于静态方法,该方法将通过强引用存储.

现在,这导致了三类lambda:

>静态方法:()=> I_dont_access_anything_nonstatic()将存储为强引用
>成员变量的闭包:()=> DoIt(字段)闭包方法将在viewmodel类中创建,操作目标是viewmodel,只要viewmodel保持活动状态,它就会保持活动状态.
>局部变量的闭包:()=> DoIt(p1)闭包将创建一个单独的类实例来存储捕获的变量.这个单独的实例将成为操作目标,并且不会有任何强烈的引用 – GC会在某些时候进行清理

重要提示:据我所知,这种行为可能会随着Roslyn:Delegate caching behavior changes in Roslyn发生变化,因此今天使用案例(2)的工作代码可能会变成使用Roslyn的非工作代码.但是,我没有测试这个假设,它可能会完全不同.

点赞