确保委托只执行一次
本文关键字:一次 执行 确保 | 更新日期: 2023-09-27 18:12:03
我有这个方法可以用作委托。由于某些原因,我希望它在每次事件触发时只做一些事情。
//In the real code those lines are in very different places
btn.Click += foo.DoOnlyOnce;
btn.Click += foo.DoOnlyOnce;
将导致DoStuff()只执行一次
private List<EventArgs> _eventArgsList = new List<EventArgs>();
public void DoOnlyOnce(object sender, EventArgs e)
{
if (!_eventArgsList.Contains(e))
{
_eventArgsList.Add(e);
DoStuff();
}
}
如果btn被点击两次,它仍然按照预期执行两次DoStuff()。
这个解决方案的一个问题是_eventArgsList
,它将无限期地跟踪eventArgs。由于GC不会像处理所有订阅后那样处理内存泄漏,因此会产生内存泄漏。
我能在委托中获得原始事件吗?
例如
GetOriginalEvent().GetInvocationList()
可以帮我决定什么时候清除这个列表。
或者,我可以用更好的方法解决这个问题吗?我最初开始尝试这样做:
var handler = new DoEventOnlyOnce(btn.Click);
handler.DoOnlyOnce += DoStuff;
handler.DoOnlyOnce += DoStuff;
所以你可以有一个原始事件的引用。但没能成功。
你可能会说我应该一直做
btn.Click -= foo.DoOnlyOnce;
btn.Click += foo.DoOnlyOnce;
但是我想要这个逻辑在DoOnlyOnce,所以我不需要依赖于正确的用法
如何使用一个简单的布尔开关:
private bool _Executed;
private void OnButtonClick(object sender, EventArgs e)
{
if(!_Executed)
{
_Executed = true;
DoOnlyOnce();
}
}
private void DoOnlyOnce()
{
Console.WriteLine("Watch carefully. You'll never see me again.");
}
我把逻辑放在事件处理程序和DoOnlyOnce()
方法之外。但我认为这只是一个品味问题(或您实际代码中的其他逻辑),如果您希望它在该方法内或在调用代码内。
好的,在得到评论和重读你的问题之后,让我们做另一种方法。我们创建一个缓存,在这里我们可以记住谁正在运行(sender
和我们获得Task
的距离)。这样就可以解决这个问题了:
private Dictionary<Object, Task> _Workers = new Dictionary<object, Task>();
private void OnButton1Click(object sender, EventArgs e)
{
StartIfNotBusy(sender);
}
private void OnButton2Click(object sender, EventArgs e)
{
StartIfNotBusy(sender);
}
private void StartIfNotBusy(object sender)
{
Task worker;
if (_Workers.TryGetValue(sender, out worker)
&& !worker.IsCompleted)
{
Console.WriteLine("Sorry, I'm currently busy.");
return;
}
_Workers[sender] = DoSomething();
}
private Task DoSomething()
{
return Task.Delay(1000).ContinueWith(_ => Console.WriteLine("Hey, I finished working."));
}