如果在 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; }
}
为什么前者失败,而后者却按预期工作?
可能相关:闭包在幕后是如何工作的? (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; }
}
这是一个老问题,但我最近偶然发现了这个话题,值得回答。
这种奇怪行为的原因源于 MVVM Light 的实现RelayCommand
。 execute 和 canexecute 处理程序存储为WeakAction _execute
and WeakFunc<bool> _canExecute
在继电器命令中。这WeakAction
是当命令由于某种原因仍被 UI 引用时允许 GC 清理视图模型的尝试。
跳过一些细节,底线是:分配一个视图模型方法作为处理程序效果很好,因为WeakAction
只要视图模型保持活动状态,它就会保持活动状态。对于动态创建的Action
,情况有所不同。如果对该操作的唯一引用位于RelayCommand
,只有弱引用存在,GC可以随时收集动作,把整个RelayCommand
变成一块死砖。
好的,是时候了解细节了。实施WeakAction
不是盲目地存储对操作的弱引用 - 这会导致许多引用消失。相反,弱的组合Delegate.Target
参考和Delegate.MethodInfo
被储存了。对于静态方法,该方法将通过强引用来存储。
现在,这导致了 lambda 的三类:
- 静态方法:
() => I_dont_access_anything_nonstatic()
将被存储为强参考
- 成员变量的闭包:
() => DoIt(field)
闭包方法将在 viewmodel 类中创建,操作目标是 viewmodel,并且只要 viewmodel 保持活动状态,该方法就会保持活动状态。
- 局部变量的闭包:
() => DoIt(p1)
闭包将创建一个单独的类实例来存储捕获的变量。这个单独的实例将成为操作目标,并且不会有任何对它的强引用 - GC 在某个时刻进行清理
重要的:据我所知,罗斯林的这种行为可能会改变:Roslyn 中的委托缓存行为发生变化因此,今天案例 (2) 的工作代码有可能变成 Roslyn 的非工作代码。然而,我没有测试这个假设,结果可能完全不同。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)