为什么这不会导致无限的事件循环
本文关键字:无限 事件 循环 为什么 | 更新日期: 2023-09-27 18:30:55
我有一个简单的应用程序,可以反转在另一个文本框中键入的任何文本。问题是,您可以修改任何一个文本框,更改将(字面上)反映在另一个文本框中。
我写了这段代码,相信它会引起问题。
private void realText_TextChanged(object sender, EventArgs e)
{
mirrorText.Text = mirror(realText.Text);
}
private void mirrorText_TextChanged(object sender, EventArgs e)
{
realText.Text = mirror(mirrorText.Text);
}
private string mirror(string text)
{
return new string(text.Reverse().ToArray()).Replace("'n'r", "'r'n");
}
然后我尝试了一下,相信它会导致无限循环(realText
变化mirrorText
,另一个事件发生,mirrorText
变化realText
,等等)。但是,除了预期的行为之外,什么也没有发生。
我当然对此感到高兴,我可以把它留在这里。或者我可以?
我很确定每当更改Text
时,都应该触发TextChanged
事件。这是事件中某些错误保护的预期行为,还是我只是运气好?此代码是否可以在另一台计算机上使用其他构建设置等行为异常?它可以很容易地修复:
private void realText_TextChanged(object sender, EventArgs e)
{
if (realText.Focused)
{
mirrorText.Text = Mirror(realText.Text);
}
}
为了安全起见,我可能会这样做,但是需要检查一下吗?(我什至不会问是否推荐。
根据注释,并且正如已经回答的那样,当您将 Text
属性设置为它已有的值时,不会引发 TextChanged
事件。
目前尚不清楚这是否是您可以放心依赖的东西。这是一个明智的优化,如果未来版本的.NET Framework放弃它,我会感到非常惊讶,但我不能代表旧版本,也不能代表第三方实现(Mono)。
为了绝对安全,我不会使用您在问题中输入的Focused
支票。我会做Text
二传手现在所做的。
private void realText_TextChanged(object sender, EventArgs e)
{
var newMirrorText = Mirror(realText.Text);
if (mirrorText.Text != newMirrorText)
mirrorText.Text = newMirrorText;
}
这具有防止无限递归的相同优点,但与您可能放入表单中的其他代码配合得更好,这些代码会因其他事件而更改文本。
它不会导致循环的原因是它检查 Text
属性是否实际更改,即新值是否等于旧值。在您的情况下,mirror
函数碰巧会自行反转,这会导致两次传递后出现相同的文本。
检查起来很容易。
首先,将两个文本框控件替换为
class T : TextBox
{
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
}
}
}
其次,在 setter 上设置断点。将这些表达式添加到"监视"窗口:
- 名字
- 发短信
- 价值
第三,启动应用程序,从某个地方复制"123"并将其粘贴到第一个文本框中。事情是这样的:
第一次休息:
- 名称:"镜像文本"
- 文本:"
- 值:"321"
第二次休息:
- 名称:"真实文本"
- 文本:"123"
- 值:"123"
第三... 哎呀,它不再坏了。为了发现为什么我们必须更深入。看看参考来源:文本框设置器没有什么不寻常的,但TextBoxBase的一个看起来很有趣:
set {
if (value != base.Text) { // Gotcha!
base.Text = value;
if (IsHandleCreated) {
// clear the modified flag
SendMessage(NativeMethods.EM_SETMODIFY, 0, 0);
}
}
}
因此,正如 hvd 已经回答的那样,原因是如果旧值和新值相同,文本框不会引发 TextChanged。我不认为这种行为会改变,至少对于winforms来说。但是,如果您想要更强大的解决方案,这里是:
private void RunOnce(ref bool flag, Action callback)
{
if (!flag)
{
try
{
flag = true;
callback();
}
finally
{
flag = false;
}
}
}
private bool inMirror;
private void realText_TextChanged(object sender, EventArgs e)
{
RunOnce(ref inMirror, () =>
{
mirrorText.Text = mirror(realText.Text);
});
}
private void mirrorText_TextChanged(object sender, EventArgs e)
{
RunOnce(ref inMirror, () =>
{
realText.Text = mirror(mirrorText.Text);
});
}
private string mirror(string text)
{
return new string(text.Reverse().ToArray()).Replace("'n'r", "'r'n");
}
附言 mirror() 将在代理项对上失败。以下是一些解决方案。
如果文本框有一个文本,并且我们尝试使用相同的文本更改它,则 TextChange 事件不会引发,因为新文本与前一个文本相同。在代码中,realText_TextChanged 事件反转文本并随之更改镜像文本。mirrorText_TextChanged 事件反转文本并尝试更改 realText。realText 已经有此文本,并且不会引发realText_TextChanged事件。