C# 来自lambda的具有构造函数参数的RelayCommand
如果在XAML文件中,我将按钮绑定到以下类中的“Command”,则单击按钮不会导致执行DoIt:C# 来自lambda的具有构造函数参数的RelayCommand,c#,lambda,closures,mvvm-light,C#,Lambda,Closures,Mvvm Light,如果在XAML文件中,我将按钮绑定到以下类中的“Command”,则单击按钮不会导致执行DoIt: class Thing() { public Thing(Foo p1) { Command = new RelayCommand(() => DoIt(p1)); } private DoIt(Foo p) { p.DoSomething(); } public ICommand Command { get; private set; } }
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; }
}
为什么前者失败了,而后者却如期发挥作用
可能相关:
编辑:为了澄清,以下内容也适用于我。然而,我仍然想知道为什么第二个例子做了我所期望的,而第一个没有
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; }
}
您的问题是调用DoIt方法是在lamda表达式创建的另一个匿名方法中。你的表情
() => DoIt(p1);
创建一个不带参数的匿名方法(在第一个大括号中没有提供变量)
我建议您使用mvvm light中的通用构造函数来创建命令:
class Thing
{
public Thing()
{
Command = new GalaSoft.MvvmLight.Command.RelayCommand<bool>(DoIt);
}
private void DoIt(bool p)
{
p.DoSomething(p);
}
public System.Windows.Input.ICommand Command { get; private set; }
}
类的东西
{
公共事物
{
Command=new-GalaSoft.MvvmLight.Command.RelayCommand(DoIt);
}
私人无效文件(bool p)
{
p、 剂量测定法(p);
}
public System.Windows.Input.ICommand命令{get;private set;}
}
然后将按钮绑定到“命令”上。这是一个老问题,但我最近偶然发现了这个话题,值得回答 这种奇怪行为的原因源自
RelayCommand
的MVVM Light实现。execute和canexecute处理程序存储为中继命令中的WeakAction\u execute
和WeakFunc\u canexecute
。WeakAction
试图在UI出于某种原因仍引用该命令时,允许对viewmodels进行GC清理
跳过一些细节,底线是:将viewmodel方法指定为处理程序非常有效,因为只要viewmodel保持活动状态,WeakAction
就会保持活动状态。对于动态创建的操作
,情况有所不同。如果对该操作的唯一引用位于RelayCommand
内,则只有弱引用存在,GC可以随时收集该操作,从而将整个RelayCommand
变成一块死砖块
好的,是时候了解细节了。WeakAction
的实现并不是盲目地存储对操作的弱引用-这将导致许多引用消失。相反,将存储弱的Delegate.Target
引用和Delegate.MethodInfo
的组合。对于静态方法,该方法将通过强引用存储
现在,这导致了三类lambda:
()=>我不访问任何东西\u nonstatic()
将作为强引用存储()=>DoIt(field)
闭包方法将在viewmodel类中创建,操作目标是viewmodel,只要viewmodel保持活动状态,闭包方法就会保持活动状态()=>DoIt(p1)
闭包将创建一个单独的类实例来存储捕获的变量。这个单独的实例将是操作目标,并且不会有任何对它的强引用-GC会在某个时候清理重要提示:据我所知,这种行为可能会随着Roslyn而改变:因此,现在使用case(2)的工作代码有可能会变成使用Roslyn的非工作代码。但是,我没有测试这个假设,它的结果可能完全不同。我不想接受绑定中的参数。也就是说,我想给它一个构造函数参数,我想在DoIt中使用该参数的值。我可以使DoIt无参数,将构造函数参数p1分配给一个字段,并使用DoIt中的字段(而不是将字段的值作为参数传递给DoIt),但我想确切地了解这里发生了什么。@。问题是创建lamda表达式会创建一个匿名内部方法,然后调用该方法。您可以这样看:
public Thing(Foo p1){Command=new RelayCommand(AnonymousMeth());}private void AnonymousMeth(){DoIt(p1);}
因此,lamda表达式生成的方法不知道如何处理p1。您可以(如正确描述的)通过使用私有字段来避免此问题。