Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/video/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# C“Lambdas:如何*不*推迟”;取消引用“;?_C#_Delegates_Lambda - Fatal编程技术网

C# C“Lambdas:如何*不*推迟”;取消引用“;?

C# C“Lambdas:如何*不*推迟”;取消引用“;?,c#,delegates,lambda,C#,Delegates,Lambda,我试图用C#委托实现撤销功能。基本上,我有一个UndoStack,它维护实现每个撤销操作的委托列表。当用户选择Edit:Undo时,该堆栈将弹出第一个委托并运行它。每个操作负责将适当的撤消委托推送到堆栈。因此,假设我有一个名为“SetCashOnHand”的方法。它如下所示: public void SetCashOnHand(int x) { int coh = this.cashOnHand; undo u = () => this.setCashOnHand(coh)

我试图用C#委托实现撤销功能。基本上,我有一个UndoStack,它维护实现每个撤销操作的委托列表。当用户选择Edit:Undo时,该堆栈将弹出第一个委托并运行它。每个操作负责将适当的撤消委托推送到堆栈。因此,假设我有一个名为“SetCashOnHand”的方法。它如下所示:

public void SetCashOnHand(int x) {
    int coh = this.cashOnHand;
    undo u = () => this.setCashOnHand(coh);
    this.cashOnHand = x;
    undoStack.Push(u);
}
static Action Curry(Action<T> method, T param) { return () => method(param); }
Curry(setCashOnHand, this.cashOnHand);
Curry(cashOnHand => setCashOnHand(3, cashOnHand), this.cashOnHand);
因此,该方法构造一个撤销委托,执行该操作,并(假设成功)将撤销委托推送到撤销堆栈上。(UndoStack非常智能,如果在撤消上下文中调用UndoStack.Push,则委托将转到重做堆栈。)

我的问题是将this.cashOnHand“缓存”到coh变量中有点烦人。我希望我能这样写:

undo u = () => this.setCashOnHand(this.cashOnHand);
当然,这不会得到现金的现值;它将延迟查找值,直到调用委托为止,因此代码将不执行任何操作。在构造委托时,除了将值填充到局部变量(如coh)中之外,还有其他方法可以“取消引用”cashOnHand吗


我真的不想听到更好的撤销方法。请将此视为一个关于委托的一般性问题,仅举一个例子来说明问题。

否,如果要在特定时间对实例变量进行求值,则必须捕获lambda之外的实例变量值

这和你写的没什么不同:

public void SetCashOnHand(int x) {
    this.cashOnHand = x;
    undoStack.Push(UndoSetCashOnHand);
}

private void UndoSetCashOnHand()
{
    this.setCashOnHand(this.cashOnHand);
}
lambda语法只是让它有点混乱,因为求值似乎是声明方法体的一部分,而实际上它是自动生成的私有函数的一部分,与任何其他函数一样得到求值


人们通常通过使用参数化函数并将传递给函数的值存储为堆栈的一部分来解决这个问题。但是,如果您想使用无参数函数,则必须在本地捕获它。

没有任何其他神奇的方法来实现您想要的功能。在执行lambda时,而不是之前,对引用进行求值。在创建lambda时,除了您正在执行的操作之外,没有自动“钩住”正在关闭的值的方法

一般来说,除了你已经在做的事情之外,没有完美的解决方案

但是,在您的具体情况下,您可以这样做:

public void SetCashOnHand(int x) {
    int coh = this.cashOnHand;
    undo u = () => this.setCashOnHand(coh);
    this.cashOnHand = x;
    undoStack.Push(u);
}
static Action Curry(Action<T> method, T param) { return () => method(param); }
Curry(setCashOnHand, this.cashOnHand);
Curry(cashOnHand => setCashOnHand(3, cashOnHand), this.cashOnHand);
如果您的方法采用其他参数,则可以如下使用:

public void SetCashOnHand(int x) {
    int coh = this.cashOnHand;
    undo u = () => this.setCashOnHand(coh);
    this.cashOnHand = x;
    undoStack.Push(u);
}
static Action Curry(Action<T> method, T param) { return () => method(param); }
Curry(setCashOnHand, this.cashOnHand);
Curry(cashOnHand => setCashOnHand(3, cashOnHand), this.cashOnHand);

委托将在将来运行。。我认为temp变量可能是处理“现在运行代码,而不是将来运行代码”的最佳方式。不过,我想您可以编写一个Bind()函数,将当前值绑定到委托

Action Bind<T>(Func<T> f, T value) { 
    return (Action)(() => f(value));
}
undoStack.Push(Bind(this.setCashOnHand, this.cashOnHand));
动作绑定(函数f,T值){
返回(动作)(()=>f(值));
}
undoStack.Push(绑定(this.setCashOnHand,this.cashOnHand));

好吧,如果您的撤销堆栈仅由将撤销其自身操作的实例引用,那么这并不重要

例如:

public class MyType
{

  private UndoStack undoStack {get;set;}

  public void SetCashOnHand(int x) {
    int coh = this.cashOnHand;
    undo u = () => this.setCashOnHand(coh);
    this.cashOnHand = x;
    undoStack.Push(u);
   }
}
那么,撤销堆栈引用的内容就无关紧要了。如果您正在注入一个UndoStack,并且对该实例的引用保存在其他地方,那么这将很重要

如果第二个是真的,我会考虑重构它,而不是试图提出某种弱引用或其他诡计来防止实例通过撤销堆栈泄漏


我想在您的情况下,每个级别都必须有自己的撤消堆栈。例如,UI层必须有一个撤销堆栈,该堆栈将弹出下一个需要撤销的实例。或者,甚至需要撤消的实例组,因为一次用户单击可能需要多个不同类型的实例按顺序执行撤消过程。

我猜您的意思是编辑,撤消。哦,您是对的。我已经解决了这个问题。哎呀,咖喱是一个更好的名字:)我认为这不管用。。。。调用UndoSetCashOnHand时,它将在undo时间获得cashOnHand的值,而不是在SetCOH时间。@Jimmy:这正是问题所在。这就是他的lambda所做的,他在问是否有办法让它不这样做。我想说的是这毫无意义。代码并不是要作为一个替代方案,它是他实际所做工作的替代表示。我明白了。抱歉误解了。这似乎是最好的选择。但这太令人困惑了,我想我还是坚持把值复制到局部变量。