在许多场景中,我们可能会遇到需要处理一种快速多次触发某事件,并处于某种阈值时,执行某一指定操作。例如,双击(即快速按鼠标左键两次)或更多击、连按键盘某键、某方法被执行数次等。其中,双击有对应的事件可以注册;然而,更多的情况是没有的类似事件方便我们注册使用的。那么,现在只能依靠我们勤劳的双手,来创造一个支持该功能的任务,并且该任务需要具有以下功能。
- 可以在任务中绑定一个方法,以便在暴击后被调用。
- 该任务可以被多次执行,包括也可以在已知事件中触发。
- 只有当任务在短时间内执行到指定次数后,所注册的方法才会被调用。
- 当短时间内执行了过多的次数,所注册的方法将不再被调用。
- 短时间的界定方式是,通过指定一个连续两次任务执行间隔的最长时间,来进行控制。
那么,这该如何实现呢?
我们需要先实现一个类,这里面会提供一个方法,允许被多次调用,并返回当下是否达到暴击标准。这个方法如果在某一定义的时间范围内,被调用达到制定数量范围内,将触发这个类中的一个事件,用以通知暴击处于激活状态。这个类还需要提供一些属性,用于定义相邻两次触发过程的时间间隔最长为多少,以及暴击的最小和最大次数。当然,为了方便了解当前状况,可以提供一些属性用以获得最近一次暴击触发的时间和连续次数。因此,以下是我们定义的类。
1 public class MultipleHitTask 2 { 3 public int Start 4 { get; set; } 5 6 public int End 7 { get; set; } 8 9 public TimeSpan Timeout 10 { get; private set; } 11 12 public DateTime LatestProcess 13 { get; private set; } 14 15 public int HitCount 16 { get; private set; } 17 18 public event EventHandler Processed; 19 20 public bool Process() 21 { 22 throw new NotImplementedException(); 23 } 24 }
在这个类中,Processed 事件用于通知当前暴击的状况,因此我们需要对事件参数进行调整,至少让提供当前连续触发次数这一信息。
1 /// <summary> 2 /// The event arguments with counting. 3 /// </summary> 4 public class IndexEventArgs: EventArgs 5 { 6 /// <summary> 7 /// Gets the index. 8 /// </summary> 9 public int Index { get; private set; } 10 11 /// <summary> 12 /// Initializes a new instance 13 /// of the IndexEventArgs class. 14 /// </summary> 15 /// <param name="index">The index.</param> 16 public IndexEventArgs(int index) 17 { 18 Index = index; 19 } 20 }
于是,前面那个 MultipleHitTask 类中的 Processed 事件的声明,就应该改成下方这样。
1 public event EventHandler<IndexEventArgs> Processed;
于是,在 Process 方法中,我们需要触发该事件。
1 var args = new IndexEventArgs(HitCount - 1); 2 Processed(this, args); 3 return true;
当然,在触发事件之前,我们还要做一些判断,用以检测是否应当激活暴击状态。
1 var now = DateTime.Now; 2 if (LatestProcess == null || now - LatestProcess > Timeout) 3 { 4 HitCount = 0; 5 } 6 7 HitCount++; 8 if (HitCount <= Start || HitCount > End) 9 { 10 return false; 11 }
于是,我们便完成了以下代码。
1 /// <summary> 2 /// Multiple hit task. 3 /// </summary> 4 public class MultipleHitTask 5 { 6 /// <summary> 7 /// Gets or sets the start index of hit to process. 8 /// </summary> 9 public int Start { get; set; } 10 11 /// <summary> 12 /// Gets or sets the end index of hit to process. 13 /// </summary> 14 public int End { get; set; } 15 16 /// <summary> 17 /// Gets the timeout. 18 /// </summary> 19 public TimeSpan Timeout { get; private set; } 20 21 /// <summary> 22 /// Gets the time of latest processing. 23 /// </summary> 24 public DateTime LatestProcess { get; private set; } 25 26 /// <summary> 27 /// Gets the hit count. 28 /// </summary> 29 public int HitCount { get; private set; } 30 31 /// <summary> 32 /// Adds or removes the event handler occured 33 /// after processing 34 /// </summary> 35 public event EventHandler<IndexEventArgs> Processed; 36 37 /// <summary> 38 /// Processes the task. 39 /// </summary> 40 /// <returns>true if match the condition to execute; 41 /// otherwise, false.</returns> 42 public bool Process() 43 { 44 var now = DateTime.Now; 45 if (LatestProcess == null 46 || now - LatestProcess > Timeout) 47 { 48 HitCount = 0; 49 } 50 51 HitCount++; 52 if (HitCount <= Start || HitCount > End) 53 return false; 54 var args = new IndexEventArgs(HitCount - 1); 55 Processed(this, args); 56 return true; 57 } 58 }
现在,我们可以在需要的地方,初始化一个这个类的对象,并绑定暴击触发时所需执行的事件,并在诸如点击、被遥测处或其它地方,调用这个对象的 Process 方法即可。
【完】
文章类型及复杂度:C# 和 .Net 进阶。
节选翻译自 MSDN 博文 Multiple Hit Task,内容有所调整。
http://blogs.msdn.com/b/kingcean/archive/2016/03/21/multiple-hit-task.aspx