具有构造函数参数的lambda的RelayCommand [英] RelayCommand from lambda with constructor parameters
问题描述
如果在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 _execute
和WeakFunc<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:
- 静态方法:
() => I_dont_access_anything_nonstatic()
将被存储为强引用 - 对成员变量的闭包:
() => DoIt(field)
将在viewmodel类中创建闭包方法,操作目标是viewmodel,并且只要viewmodel保持活动状态,它就会保持活动状态. - 局部变量的闭包:
() => DoIt(p1)
闭包将创建一个单独的类实例来存储捕获的变量.这个单独的实例将成为操作目标,并且对此没有任何强有力的参考-GC会在某个时候清理
- static method:
() => I_dont_access_anything_nonstatic()
will be stored as a strong reference - 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. - 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
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屋!