具有构造函数参数的lambda的RelayCommand [英] RelayCommand from lambda with constructor parameters

查看:178
本文介绍了具有构造函数参数的lambda的RelayCommand的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

If, in a XAML file, I bind a Button to "Command" from the following class, then clicking the Button does not cause DoIt to be executed:

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内部的方法调用,它将起作用:

However, it does work if I initialize a field from p1 and pass the field as a parameter to the method call inside the 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; }
}

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

Why does the former fail, but the latter work as expected?

可能相关:闭包在幕后如何工作? (C#)

为澄清起见,以下内容也对我有用.但是,我仍然想知道为什么第二个例子能达到我的预期,而第一个例子却没有.

To clarify, the following would also work for me. However, I would still like to know why the second example did what I expected, but the first one did not.

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; }
}

推荐答案

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

It's an old question but I recently stumbled upon this topic and it's worth answering.

此异常行为的原因源自RelayCommand的MVVM Light实现. execute和canexecute处理程序在中继命令中存储为WeakAction _executeWeakFunc<bool> _canExecute. WeakAction试图在由于某种原因UI仍引用该命令时允许对视图模型进行GC清理.

The reason for this strange behavior originates from the MVVM Light implementation of RelayCommand. The execute and canexecute handlers are stored as WeakAction _execute and WeakFunc<bool> _canExecute in the relay command. The WeakAction is an attempt to allow the GC cleanup of viewmodels when the command is still referenced by the UI for some reason.

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

Skipping some details, the bottom line is: assigning a viewmodel method as handler works great, because the WeakAction will stay alive as long as the viewmodel stays alive. For a dynamically created Action, the situation is different. If the only reference to that action is inside the RelayCommand, only a weak reference exists and GC can collect the action at any time, turning the whole RelayCommand into a dead brick.

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

Ok, time for the details. The implementation of WeakAction is not blindly storing a weak reference to the action - this would lead to many disappearing references. Instead, a combination of a weak Delegate.Target reference and an Delegate.MethodInfo is stored. For a static method, the method will be stored by strong reference.

现在,这导致了三类lambda:

Now, this leads to three categories of lambda:

  1. 静态方法:() => I_dont_access_anything_nonstatic()将被存储为强引用
  2. 对成员变量的闭包:() => DoIt(field)将在viewmodel类中创建闭包方法,操作目标是viewmodel,并且只要viewmodel保持活动状态,它就会保持活动状态.
  3. 局部变量的闭包:() => DoIt(p1)闭包将创建一个单独的类实例来存储捕获的变量.这个单独的实例将成为操作目标,并且对此没有任何强有力的参考-GC会在某个时候清理
  1. static method: () => I_dont_access_anything_nonstatic() will be stored as a strong reference
  2. closure on member variables: () => DoIt(field) the closure method will be created in the viewmodel class, the action target is the viewmodel and will stay alive as long as the viewmodel stays alive.
  3. closure on local variables: () => DoIt(p1) the closure will create a separate class instance to store the captured variables. This separate instance will be the action target and there won't be any strong reference to it - GC cleans up at some point

重要提示:据我所知,这种行为可能会随Roslyn改变:

Important: as far as I can tell, this behavior might change with Roslyn: Delegate caching behavior changes in Roslyn so there is a chance that todays working code with case (2) turns into non-working code with Roslyn. However, I didn't test this assumption, it might work out completely different.

这篇关于具有构造函数参数的lambda的RelayCommand的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆