如果在XAML文件中,将以下类中的Button绑定(bind)到“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 _executeWeakFunc<bool> _canExecute。当出于某种原因UI仍然引用命令时,WeakAction试图允许对 View 模型进行GC清理。

跳过一些细节,最重要的是:为处理程序分配一个viewmodel方法非常有效,因为只要viewmodel保持 Activity 状态,WeakAction就会保持 Activity 状态。对于动态创建的Action,情况有所不同。如果对该 Action 的唯一引用位于RelayCommand内部,则仅存在一个弱引用,GC可以随时收集该 Action ,从而将整个RelayCommand变成一块死砖。

好的,是时候了。 WeakAction的实现不会盲目地存储对该操作的弱引用-这将导致许多引用消失。而是,存储弱Delegate.Target引用和Delegate.MethodInfo的组合。对于静态方法,该方法将通过强引用进行存储。

现在,这导致了三类lambda:

  • 静态方法:() => I_dont_access_anything_nonstatic()将被存储为强引用
  • 成员变量上的
  • 闭包:() => DoIt(field)闭包方法将在viewmodel类中创建,操作目标是viewmodel,并且只要viewmodel保持 Activity 状态,它就会保持 Activity 状态。
  • 局部变量上的
  • 闭包:() => DoIt(p1)闭包将创建一个单独的类实例来存储捕获的变量。这个单独的实例将成为操作目标,并且对其没有任何强引用-GC会在某个时候清理

  • 重要提示:据我所知,可能会随着Roslyn的改变而改变:Delegate caching behavior changes in Roslyn,所以今天情况(2)的工作代码可能会变成与Roslyn不工作的代码。但是,我没有检验这个假设,它可能会完全不同。

    关于c# - 具有构造函数参数的lambda的RelayCommand,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46811428/

    10-16 09:09