C# Lambdas:如何*不*推迟“取消引用”?
我正在尝试使用 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 的现值;它推迟查找值,直到调用委托为止,因此代码最终什么也不做。除了将值填充到像 coh 这样的局部变量中之外,有什么方法可以在构造委托时“取消引用”cashOnHand 吗?
我并不是真的有兴趣听到更好的撤消方法。请将此视为有关代表的一般问题,撤消只是一个示例,以使问题更加清晰。
I'm trying to implement undo functionality with C# delegates. Basically, I've got an UndoStack that maintains a list of delegates implementing each undo operation. When the user chooses Edit:Undo, this stack pops off the first delegate and runs it. Each operation is responsible for pushing an appropriate undo delegate unto the stack. So suppose I have a method called "SetCashOnHand." It looks like this:
public void SetCashOnHand(int x) {
int coh = this.cashOnHand;
undo u = () => this.setCashOnHand(coh);
this.cashOnHand = x;
undoStack.Push(u);
}
So this method constructs an undo delegate, does the operation, and (assuming it succeeds) pushes the undo delegate onto the UndoStack. (UndoStack is smart enough that if undoStack.Push is called in the context of an undo, the delegate goes on the redo stack instead.)
My trouble is that it's a little annoying to "cache" this.cashOnHand into the coh variable. I wish I could just write this:
undo u = () => this.setCashOnHand(this.cashOnHand);
But of course that won't get the present value of cashOnHand; it defers looking up the value until the delegate is called, so the code winds up doing nothing. Is there any way I can "dereference" cashOnHand when constructing the delegate, other than stuffing the value into a local variable like coh?
I'm not really interested in hearing about better ways to do Undo. Please think of this as a generic question about delegates, with Undo just the example to make the question more clear.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
一般来说,除了您已经在做的事情之外,没有完美的解决方案。
但是,在您的具体情况下,您可以创建一个 currier,如下所示:
您可以使用它像这样:
如果你的方法需要其他参数,你可以像这样使用它:
In general, there is no perfect solution to this other than what you're already doing.
However, in your specific case, you can make a currier, like this:
You can use it like this:
If your method takes other parameters, you can use it like this:
不,如果您希望在特定时间评估实例变量的值,则必须在 lambda 之外捕获实例变量的值。
这与您编写的代码没有什么不同:
lambda 语法只是让它有点混乱,因为评估似乎是声明方法体的一部分,而实际上它是自动生成的一部分像任何其他函数一样进行评估的私有函数。
人们通常解决这个问题的方法是使用参数化函数并将传递给函数的值作为堆栈的一部分存储。但是,如果您想使用无参数函数,则必须在本地捕获它。
No, you'll have to capture the value of your instance variable outside of the lambda if you want it evaluated at that particular time.
It's no different than if you'd written this:
The lambda syntax just make it slightly confusing since the evaluation appears to be a part of the declaring method body, when in reality it's part of an automatically-generated private function that gets evaluated just like any other function.
The way people generally get around this is by using a parameterized function and storing the value that gets passed to the function as part of the stack. If, however, you want to go with a parameter-less function, you'll have to capture it in a local.
确实没有任何其他神奇的方法可以做你想做的事。该引用是在执行 lambda 时而不是之前计算的。除了您正在做的事情之外,没有一种自动方法可以“挂钩”创建 lambda 时关闭的值。
There isn't really any other magical way to do what you'd like. The reference is evaluated when the lambda is executed, not before. There isn't an automatic way to "hook" the value that's being closed over when the lambda was created other than what you're doing.
委托将在未来运行..我认为 temp 变量可能是处理“现在运行代码,而不是将来运行代码”的最佳方法。但是,我想您可以编写一个 Bind() 函数将当前值绑定到委托。
The delegate is running in the future .. I think the temp variable is probably the best way to handle "run code now, instead of in the future." However, I guess you could write a Bind() function that binds a current value to a delegate..
好吧,如果您的撤消堆栈仅由将撤消其自身操作的实例引用,那么这并不重要。
例如:
那么 UndoStack 引用什么并不重要。如果您要注入 UndoStack 并且对此实例的引用保存在其他地方,那么这很重要。
如果第二个是真的,我会考虑重构它,而不是尝试提出某种弱引用或其他欺骗手段来防止实例通过撤消堆栈泄漏。
我想在你的情况下,每个级别都必须有自己的撤消堆栈。例如,UI 层必须有一个撤消堆栈,它将弹出接下来需要撤消的实例。或者,甚至,需要撤消的实例组,因为一次用户单击可能需要不同类型的多个实例按顺序经历撤消过程。
Well, if your Undo stack is referenced only by the instance that will be undoing its own actions, then it doesn't matter.
For instance:
then it doesn't matter what the UndoStack references. If you're injecting an UndoStack and references to this instance are held elsewhere then it would matter.
If the second is true, I'd see about refactoring this rather than attempting to come up with some kind of weak reference or other chicanery to prevent instances being leaked via the undo stack.
I suppose in your case each level would have to have its own Undo stack. For instance, the UI layer would have to have an undo stack which would pop off the instance which needs to be undone next. Or, even, the groups of instances which need to undo, as one user click may require multiple instances of different types go through an undo procedure in order.