在许多场景中,我们可能会遇到需要处理一种快速多次触发某事件,并处于某种阈值时,执行某一指定操作。例如,双击(即快速按鼠标左键两次)或更多击、连按键盘某键、某方法被执行数次等。其中,双击有对应的事件可以注册;然而,更多的情况是没有的类似事件方便我们注册使用的。那么,现在只能依靠我们勤劳的双手,来创造一个支持该功能的任务,并且该任务需要具有以下功能。
- 可以在任务中绑定一个方法,以便在暴击后被调用。
- 该任务可以被多次执行,包括也可以在已知事件中触发。
- 只有当任务在短时间内执行到指定次数后,所注册的方法才会被调用。
- 当短时间内执行了过多的次数,所注册的方法将不再被调用。
- 短时间的界定方式是,通过指定一个连续两次任务执行间隔的最长时间,来进行控制。
那么,这该如何实现呢?
我们需要先实现一个类,这里面会提供一个方法,允许被多次调用,并返回当下是否达到暴击标准。这个方法如果在某一定义的时间范围内,被调用达到制定数量范围内,将触发这个类中的一个事件,用以通知暴击处于激活状态。这个类还需要提供一些属性,用于定义相邻两次触发过程的时间间隔最长为多少,以及暴击的最小和最大次数。当然,为了方便了解当前状况,可以提供一些属性用以获得最近一次暴击触发的时间和连续次数。因此,以下是我们定义的类。
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