2015-09-10 84 views
0

我有这个方法可以用作委托。由于原因,我希望每次事件触发时,它只做一次事情。确保委托只执行一次

//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这个逻辑,所以我不必依靠正确的用法。

+3

因此,你真的只想附加_handler_一次,对吗?为什么它首先被多次添加?似乎_that_是你的控制应该在哪里。 –

+3

请注意[适当的解决方案](http://stackoverflow.com/questions/2697247/how-to-determine-if-an-event-is-already-subscribed)是你似乎不愿意实现的。 –

+0

传统代码...事件被添加和分离,有时以非谓词顺序(因为这也有时发生在事件中......)。我想在一个专门的课堂上解决这个问题。 –

回答

0

如何使用一个简单的布尔开关:

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.")); 
} 
+1

不回答该问题。该方法应该在每次点击按钮时执行一次。 – Henrik

+0

如果它附加在不同的事件(按钮)上,它也应该为每个事件执行。 –