繁体   English   中英

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

[英]C# Lambdas: How *Not* to Defer “Dereference”?

我正在尝试使用C#委托实现撤消功能。 基本上,我有一个UndoStack,它维护一个实现每个撤消操作的委托列表。 当用户选择编辑:撤消时,此堆栈会弹出第一个代理并运行它。 每个操作都负责将适当的撤消委托推送到堆栈。 假设我有一个名为“SetCashOnHand”的方法。 它看起来像这样:

public void SetCashOnHand(int x) {
    int coh = this.cashOnHand;
    undo u = () => this.setCashOnHand(coh);
    this.cashOnHand = x;
    undoStack.Push(u);
}

因此,此方法构造撤消委托,执行操作,并(假设成功)将撤消委托推送到UndoStack。 (UndoStack足够智能,如果在撤消的上下文中调用undoStack.Push,则代理会转到重做堆栈。)

我的麻烦是,将this.cashOnHand“缓存”到coh变量中有点烦人。 我希望我能写下这个:

undo u = () => this.setCashOnHand(this.cashOnHand);

但当然这不会得到cashOnHand的值; 它会在调用委托之前推迟查找值,因此代码最终无所事事。 有没有什么办法可以在构造委托时“取消引用”cashOnHand,除了将值填充到像coh这样的局部变量中?

我对听到更好的撤销方法并不感兴趣。 请将此视为关于代理人的一般性问题,使用撤消只是举例来使问题更加清晰。

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

然而,在特定情况下,你可以做一个宿舍楼里 ,就像这样:

static Action Curry(Action<T> method, T param) { return () => method(param); }

你可以像这样使用它:

Curry(setCashOnHand, this.cashOnHand);

如果您的方法采用其他参数,您可以像这样使用它:

Curry(cashOnHand => setCashOnHand(3, cashOnHand), this.cashOnHand);

不,如果您希望在特定时间进行评估,则必须在lambda之外捕获实例变量的值。

这与你写这篇文章没有什么不同:

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

private void UndoSetCashOnHand()
{
    this.setCashOnHand(this.cashOnHand);
}

lambda语法只是让它有点混乱,因为评估似乎是声明方法体的一部分,而实际上它是自动生成的私有函数的一部分,它像任何其他函数一样被评估。

人们通常会解决这个问题的方法是使用参数化函数并存储作为堆栈一部分传递给函数的值。 但是,如果你想要去一个参数- 更少的功能,你必须抓住它在一个地方。

没有任何其他神奇的方法可以做你想做的事。 执行lambda时评估引用,而不是之前。 当创建lambda而不是你正在做的事情时,没有一种自动的方法来“挂钩”被关闭的值。

委托在未来运行。我认为临时变量可能是处理“现在运行代码而不是将来”的最佳方法。 但是,我猜你可以编写一个Bind()函数,将当前值绑定到委托。

Action Bind<T>(Func<T> f, T value) { 
    return (Action)(() => f(value));
}
undoStack.Push(Bind(this.setCashOnHand, this.cashOnHand));

好吧,如果您的Undo堆栈仅由将撤消其自己的操作的实例引用,则无关紧要。

例如:

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引用的内容并不重要。 如果您正在注入UndoStack,并且此实例的引用被保存在其他位置,那么它就很重要了。

如果第二个是真的,我会看到重构这个而不是试图想出某种弱引用或其他的chicanery来防止实例通过撤销堆栈泄露。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM