更好的解决方案、计时器、秒表、时间跨度
Better solution, timer, stopwatch, timespan
我正在开发用于跟踪各种活动持续时间的小工具。
在此示例中,我们有 3 个活动,驾驶、步行和等待。
每个活动都是 Form1 上的一个按钮
示例:
- 单击驱动器按钮,秒表 "SW" 和计时器 "Tmr" 开始计时 "Drive" 时间。
- 5 秒后,我单击等待按钮,SW 和 Tmr 停止,SW1 和 Tmr1 启动并计算时间 "Wait" activity。
- 再次单击按钮 Drive,SW1 和 Tmr1 停止,SW 和 Tmr 启动,时间从第 5 秒恢复
等等,可以是一项或多项活动。在测量结束时,我有每个 activity.
的总持续时间
下面这段代码实际上运行良好。从 Form1 调用函数,开始测量,稍后我在 public 个可用变量中有值。
模块:
Dim SW, SW1, SW2 As New Stopwatch
Dim WithEvents Tmr, Tmr1, Tmr2 As New Timer
Dim stws() = {SW, SW1, SW2}
Dim tmrs() = {Tmr, Tmr1, Tmr2}
Public Drive, Walk, Wait As String
Public Function WhichButton(btn As Button)
WhichButton = btn.Text
Select Case WhichButton
Case "Drive"
For Each s As Stopwatch In stws
s.Stop()
Next
For Each t As Timer In tmrs
t.Stop()
Next
SW.Start()
Tmr.Start()
Case "Wait"
For Each s As Stopwatch In stws
s.Stop()
Next
For Each t As Timer In tmrs
t.Stop()
Next
SW.Start()
Tmr1.Start()
Case "Walk"
For Each s As Stopwatch In stws
s.Stop()
Next
For Each t As Timer In tmrs
t.Stop()
Next
SW2.Start()
Tmr2.Start()
End Select
End Function
Private Sub Tmr_Tick(sender As Object, e As System.EventArgs) Handles Tmr.Tick
Dim elapsed As TimeSpan = SW.Elapsed
Drive = $"{elapsed.Hours:00}:{elapsed.Minutes:00}.{elapsed.Seconds:00}"
End Sub
Private Sub Tmr1_Tick(sender As Object, e As System.EventArgs) Handles Tmr1.Tick
Dim elapsed As TimeSpan = SW1.Elapsed
Walk = $"{elapsed.Hours:00}:{elapsed.Minutes:00}.{elapsed.Seconds:00}"
End Sub
Private Sub Tmr2_Tick(sender As Object, e As System.EventArgs) Handles Tmr2.Tick
Dim elapsed As TimeSpan = SW2.Elapsed
Wait = $"{elapsed.Hours:00}:{elapsed.Minutes:00}.{elapsed.Seconds:00}"
End Sub
我来这里是因为我对这个解决方案不满意,而且我不了解高级解决方案。这里的问题是我可以有 X 个按钮,可以添加新按钮或删除几个按钮,这取决于情况,我不想为每个按钮编写代码块。此外,如果我更改按钮的文本 属性,Select 大小写将不起作用。
所以我想为每个按钮动态创建计时器和秒表。
我想从这个开始:
Dim timers As List(Of Timer) = New List(Of Timer)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For Each btn As Button In Panel1.Controls.OfType(Of Button)
timers.Add(New Timer() With {.Tag = btn.Name})
AddHandler btn.Click, AddressOf Something
Next
End Sub
Public Sub Something(sender As Object, e As EventArgs)
Dim btn = DirectCast(sender, Button)
Dim tmr As Timer = timers.SingleOrDefault(Function(t) t.Tag IsNot Nothing AndAlso t.Tag.ToString = btn.Name)
End Sub
在这里我可以通过标签引用定时器 属性 但我不知道如何实现秒表和时间跨度。
感谢阅读,如有任何帮助,建议、伪代码、代码示例,我们将不胜感激。
首先,使用三个 Timers
没有意义。一个 Timer
可以处理所有 3 次。其次,根据您发布的内容,使用 Timer
毫无意义。我认为 Timer
有用的唯一原因是不断在 UI 中显示当前经过的时间,但您没有这样做。如果您不打算显示这些变量,则重复设置这些 String
变量是没有意义的。只需在需要时从适当的 Stopwatch
中获取 Elapsed
值即可。
至于你的 Buttons'
Click
事件处理程序,它也很糟糕。通用事件处理程序的全部意义在于,您希望对每个对象执行相同的操作,因此您只需编写一次代码。如果您最终为该公共事件处理程序中的每个对象编写单独的代码,那么这就失去了意义,并使您的代码更复杂而不是更少。您应该为每个 Button
.
使用单独的事件处理程序
如果您要使用通用事件处理程序,至少要提取通用代码。在所有三个 Case
块中都有相同的两个 For Each
循环。这应该在 Select Case
之前完成,然后只在每个 Case
.
中启动适当的 Stopwatch
不过我认为您不应该使用 Buttons
。你实际上应该使用 RadioButtons
。您可以将它们的 Appearance
属性 设置为 Button
,然后它们看起来就像普通的 Buttons
,但仍然表现得像 RadioButtons
。当您单击一个时,它会保留按下的外观以指示它已被选中,单击另一个将释放先前按下的那个。在这种情况下,您的代码可能如下所示:
Private ReadOnly driveStopwatch As New Stopwatch
Private ReadOnly waitStopwatch As New Stopwatch
Private ReadOnly walkStopwatch As New Stopwatch
Private Sub driveRadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles driveRadioButton.CheckedChanged
If driveRadioButton.Checked Then
driveStopwatch.Start()
Else
driveStopwatch.Stop()
End If
End Sub
Private Sub waitRadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles waitRadioButton.CheckedChanged
If waitRadioButton.Checked Then
waitStopwatch.Start()
Else
waitStopwatch.Stop()
End If
End Sub
Private Sub walkRadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles walkRadioButton.CheckedChanged
If walkRadioButton.Checked Then
walkStopwatch.Start()
Else
walkStopwatch.Stop()
End If
End Sub
因为选中 RadioButton
会自动取消选中任何其他,每个 CheckedChanged
事件处理程序只需要担心自己的 Stopwatch
。
如果你想在它停止时显示特定 Stopwatch
的经过时间,你可以在它停止时这样做,例如
Private Sub driveRadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles driveRadioButton.CheckedChanged
If driveRadioButton.Checked Then
driveStopwatch.Start()
Else
driveStopwatch.Stop()
driveLabel.Text = driveStopwatch.Elapsed.ToString("hh\:mm\:ss")
End If
End Sub
我认为 TimeSpan.ToString
的重载首先在 .NET 4.5 中可用,因此除非您的目标是 .NET 4.0 或更早版本,否则您应该使用它。
如果你确实想不断显示当前经过的时间,那么正如我所说,你只需要一个Timer
。您只需让它一直 运行 并根据当前 运行ning 的 Stopwatch
适当更新,例如
Private Sub displayTimer_Tick(sender As Object, e As EventArgs) Handles displayTimer.Tick
If driveStopwatch.IsRunning Then
driveLabel.Text = driveStopwatch.Elapsed.ToString("hh\:mm\:ss")
ElseIf waitStopwatch.IsRunning Then
waitLabel.Text = waitStopwatch.Elapsed.ToString("hh\:mm\:ss")
ElseIf walkStopwatch.IsRunning Then
walkLabel.Text = walkStopwatch.Elapsed.ToString("hh\:mm\:ss")
End If
End Sub
您还没有向我们展示您是如何显示经过时间的,所以这只是一个猜测。在这个场景中,当 Stopwatch
停止时,您绝对应该更新 Label
,因为 Timer
不会在下一个 Tick
上更新 Label
。
你可能想要一个 Button
可以停止的地方 and/or 重置所有三个 Stopwatches
。这意味着在所有三个 RadioButtons
上将 Checked
设置为 False
,然后在所有三个 Stopwatches
上调用 Reset
。您可能也想 clear/reset Labels
。
像这样使用 RadioButtons
也存在潜在问题。如果您的 RadioButtons
之一在 Tab 键顺序中排在第一位,那么在您加载表单时它将默认获得焦点。聚焦 RadioButton
将检查它,因此这意味着您将默认启动 Stopwatch
。如果这不是您想要的,请确保其他控件位于 Tab 键顺序中的第一个。如果由于某种原因你不能这样做,处理表单的 Shown
事件,将 ActiveControl
设置为 Nothing
,取消选中 RadioButton
并重置相应的 Stopwatch
和 Label
.
作为最后一条一般信息,请注意我已经为所有内容命名,这样即使事先不了解该项目的人也不会怀疑所有内容是什么以及它们的用途。 SW
、SW1
和 SW2
这样的名字是不好的。即使您意识到 SW
意味着 Stopwatch
,您也不知道每个值的实际用途。在 Intellisense 的今天,它只是懒惰地使用这样的名称。每个有经验的开发人员都可以告诉你一个故事,关于一段时间后回过头来阅读他们自己的代码,却不知道他们所说的各种东西的意思。不要陷入那个陷阱,确保你早日养成良好的习惯。
编辑:
作为奖励,您可以通过以下方式正确使用通用事件处理程序。首先,定义一个自定义 Stopwatch
class 具有关联的 Label
:
Public Class StopwatchEx
Inherits Stopwatch
Public Property Label As Label
End Class
一旦建立关联,您就会自动知道使用哪个 Label
来显示 Stopwatch
的经过时间。接下来,定义一个具有关联 Stopwatch
:
的自定义 RadioButton
class
Public Class RadioButtonEx
Inherits RadioButton
Public Property Stopwatch As StopwatchEx
End Class
接下来,在您的表单上使用自定义 class 而不是标准 RadioButtons
。您可以直接从工具箱添加它们(您的自定义控件将在构建项目后自动添加),或者您可以编辑设计器代码文件并更改代码中控件的类型。后一种选择存在一定的风险,因此请务必事先创建备份。完成后,更改 Stopwatches
的类型并处理表单的 Load
事件以创建关联:
Private ReadOnly driveStopwatch As New StopwatchEx
Private ReadOnly waitStopwatch As New StopwatchEx
Private ReadOnly walkStopwatch As New StopwatchEx
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Associate Stopwatches with RadioButtons
driveRadioButton.Stopwatch = driveStopwatch
waitRadioButton.Stopwatch = waitStopwatch
walkRadioButton.Stopwatch = walkStopwatch
'Associate Labels with Stopwatches
driveStopwatch.Label = driveLabel
waitStopwatch.Label = waitLabel
walkStopwatch.Label = walkLabel
End Sub
您现在可以使用一个方法来处理所有三个 CheckedChanged
事件 RadioButtons
因为您现在可以对所有三个执行完全相同的操作:
Private Sub RadioButtons_CheckedChanged(sender As Object, e As EventArgs) Handles driveRadioButton.CheckedChanged,
waitRadioButton.CheckedChanged,
walkRadioButton.CheckedChanged
Dim rb = DirectCast(sender, RadioButtonEx)
Dim sw = rb.Stopwatch
If rb.Checked Then
sw.Start()
Else
sw.Stop()
sw.Label.Text = sw.Elapsed.ToString("hh\:mm\:ss")
End If
End Sub
引发事件的 RadioButton
会告诉您要使用哪个 Stopwatch
,那个会告诉您要使用哪个 Label
,因此无需为每个事件编写不同的代码。
Timer
的Tick
事件处理程序也可以用通用代码处理每个Stopwatch
:
Private Sub displayTimer_Tick(sender As Object, e As EventArgs) Handles displayTimer.Tick
For Each sw In {driveStopwatch, waitStopwatch, walkStopwatch}
If sw.IsRunning Then
sw.Label.Text = sw.Elapsed.ToString("hh\:mm\:ss")
Exit For
End If
Next
End Sub
您可以在 class 级别创建数组,但是,由于它只在这个地方使用,因此在这里创建它是有意义的。性能影响微不足道,它通过在使用它们的地方创建东西使代码更具可读性。
请注意,我在这段代码中确实使用了变量名的缩写。这是出于两个原因。首先,它们是在不同时间引用不同对象的变量。这意味着不可能使用特定于对象用途的名称。您可以使用基于上下文的名称,例如currentRadioButton
,但由于第二个原因,我在这里不这样做。
第二个原因是它们是在非常有限的范围内使用的局部变量。 rb
和 sw
变量的使用不超过声明它们的几行,因此很难不理解它们是什么。如果你这样命名一个字段,那么当你在代码中看到它时,你必须去别处寻找它是什么。在这段代码中,如果您正在查看其中一个变量的用法,那么声明也在视线范围内,因此您必须视而不见才能看不到您正在处理的是什么类型。基本上,如果一个变量的使用距离它的声明很远,那么我建议使用一个有意义的、描述性的名称。如果它只在其声明的几行内使用,那么一个简短的名称就可以了。我通常倾向于使用类型的首字母,就像我在这里所做的那样。如果您需要该类型的多个局部变量,我通常更喜欢使用描述性名称来消除它们的歧义,而不是使用数字。但是,有时确实没有特定目的的方法来做到这一点,在这种情况下,数字是可以的,例如在没有上下文的情况下比较两个 Strings
可能会使用 s1
和 s2
作为变量名称。
我正在开发用于跟踪各种活动持续时间的小工具。
在此示例中,我们有 3 个活动,驾驶、步行和等待。
每个活动都是 Form1 上的一个按钮
示例:
- 单击驱动器按钮,秒表 "SW" 和计时器 "Tmr" 开始计时 "Drive" 时间。
- 5 秒后,我单击等待按钮,SW 和 Tmr 停止,SW1 和 Tmr1 启动并计算时间 "Wait" activity。
- 再次单击按钮 Drive,SW1 和 Tmr1 停止,SW 和 Tmr 启动,时间从第 5 秒恢复
等等,可以是一项或多项活动。在测量结束时,我有每个 activity.
的总持续时间下面这段代码实际上运行良好。从 Form1 调用函数,开始测量,稍后我在 public 个可用变量中有值。
模块:
Dim SW, SW1, SW2 As New Stopwatch
Dim WithEvents Tmr, Tmr1, Tmr2 As New Timer
Dim stws() = {SW, SW1, SW2}
Dim tmrs() = {Tmr, Tmr1, Tmr2}
Public Drive, Walk, Wait As String
Public Function WhichButton(btn As Button)
WhichButton = btn.Text
Select Case WhichButton
Case "Drive"
For Each s As Stopwatch In stws
s.Stop()
Next
For Each t As Timer In tmrs
t.Stop()
Next
SW.Start()
Tmr.Start()
Case "Wait"
For Each s As Stopwatch In stws
s.Stop()
Next
For Each t As Timer In tmrs
t.Stop()
Next
SW.Start()
Tmr1.Start()
Case "Walk"
For Each s As Stopwatch In stws
s.Stop()
Next
For Each t As Timer In tmrs
t.Stop()
Next
SW2.Start()
Tmr2.Start()
End Select
End Function
Private Sub Tmr_Tick(sender As Object, e As System.EventArgs) Handles Tmr.Tick
Dim elapsed As TimeSpan = SW.Elapsed
Drive = $"{elapsed.Hours:00}:{elapsed.Minutes:00}.{elapsed.Seconds:00}"
End Sub
Private Sub Tmr1_Tick(sender As Object, e As System.EventArgs) Handles Tmr1.Tick
Dim elapsed As TimeSpan = SW1.Elapsed
Walk = $"{elapsed.Hours:00}:{elapsed.Minutes:00}.{elapsed.Seconds:00}"
End Sub
Private Sub Tmr2_Tick(sender As Object, e As System.EventArgs) Handles Tmr2.Tick
Dim elapsed As TimeSpan = SW2.Elapsed
Wait = $"{elapsed.Hours:00}:{elapsed.Minutes:00}.{elapsed.Seconds:00}"
End Sub
我来这里是因为我对这个解决方案不满意,而且我不了解高级解决方案。这里的问题是我可以有 X 个按钮,可以添加新按钮或删除几个按钮,这取决于情况,我不想为每个按钮编写代码块。此外,如果我更改按钮的文本 属性,Select 大小写将不起作用。 所以我想为每个按钮动态创建计时器和秒表。
我想从这个开始:
Dim timers As List(Of Timer) = New List(Of Timer)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For Each btn As Button In Panel1.Controls.OfType(Of Button)
timers.Add(New Timer() With {.Tag = btn.Name})
AddHandler btn.Click, AddressOf Something
Next
End Sub
Public Sub Something(sender As Object, e As EventArgs)
Dim btn = DirectCast(sender, Button)
Dim tmr As Timer = timers.SingleOrDefault(Function(t) t.Tag IsNot Nothing AndAlso t.Tag.ToString = btn.Name)
End Sub
在这里我可以通过标签引用定时器 属性 但我不知道如何实现秒表和时间跨度。 感谢阅读,如有任何帮助,建议、伪代码、代码示例,我们将不胜感激。
首先,使用三个 Timers
没有意义。一个 Timer
可以处理所有 3 次。其次,根据您发布的内容,使用 Timer
毫无意义。我认为 Timer
有用的唯一原因是不断在 UI 中显示当前经过的时间,但您没有这样做。如果您不打算显示这些变量,则重复设置这些 String
变量是没有意义的。只需在需要时从适当的 Stopwatch
中获取 Elapsed
值即可。
至于你的 Buttons'
Click
事件处理程序,它也很糟糕。通用事件处理程序的全部意义在于,您希望对每个对象执行相同的操作,因此您只需编写一次代码。如果您最终为该公共事件处理程序中的每个对象编写单独的代码,那么这就失去了意义,并使您的代码更复杂而不是更少。您应该为每个 Button
.
如果您要使用通用事件处理程序,至少要提取通用代码。在所有三个 Case
块中都有相同的两个 For Each
循环。这应该在 Select Case
之前完成,然后只在每个 Case
.
Stopwatch
不过我认为您不应该使用 Buttons
。你实际上应该使用 RadioButtons
。您可以将它们的 Appearance
属性 设置为 Button
,然后它们看起来就像普通的 Buttons
,但仍然表现得像 RadioButtons
。当您单击一个时,它会保留按下的外观以指示它已被选中,单击另一个将释放先前按下的那个。在这种情况下,您的代码可能如下所示:
Private ReadOnly driveStopwatch As New Stopwatch
Private ReadOnly waitStopwatch As New Stopwatch
Private ReadOnly walkStopwatch As New Stopwatch
Private Sub driveRadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles driveRadioButton.CheckedChanged
If driveRadioButton.Checked Then
driveStopwatch.Start()
Else
driveStopwatch.Stop()
End If
End Sub
Private Sub waitRadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles waitRadioButton.CheckedChanged
If waitRadioButton.Checked Then
waitStopwatch.Start()
Else
waitStopwatch.Stop()
End If
End Sub
Private Sub walkRadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles walkRadioButton.CheckedChanged
If walkRadioButton.Checked Then
walkStopwatch.Start()
Else
walkStopwatch.Stop()
End If
End Sub
因为选中 RadioButton
会自动取消选中任何其他,每个 CheckedChanged
事件处理程序只需要担心自己的 Stopwatch
。
如果你想在它停止时显示特定 Stopwatch
的经过时间,你可以在它停止时这样做,例如
Private Sub driveRadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles driveRadioButton.CheckedChanged
If driveRadioButton.Checked Then
driveStopwatch.Start()
Else
driveStopwatch.Stop()
driveLabel.Text = driveStopwatch.Elapsed.ToString("hh\:mm\:ss")
End If
End Sub
我认为 TimeSpan.ToString
的重载首先在 .NET 4.5 中可用,因此除非您的目标是 .NET 4.0 或更早版本,否则您应该使用它。
如果你确实想不断显示当前经过的时间,那么正如我所说,你只需要一个Timer
。您只需让它一直 运行 并根据当前 运行ning 的 Stopwatch
适当更新,例如
Private Sub displayTimer_Tick(sender As Object, e As EventArgs) Handles displayTimer.Tick
If driveStopwatch.IsRunning Then
driveLabel.Text = driveStopwatch.Elapsed.ToString("hh\:mm\:ss")
ElseIf waitStopwatch.IsRunning Then
waitLabel.Text = waitStopwatch.Elapsed.ToString("hh\:mm\:ss")
ElseIf walkStopwatch.IsRunning Then
walkLabel.Text = walkStopwatch.Elapsed.ToString("hh\:mm\:ss")
End If
End Sub
您还没有向我们展示您是如何显示经过时间的,所以这只是一个猜测。在这个场景中,当 Stopwatch
停止时,您绝对应该更新 Label
,因为 Timer
不会在下一个 Tick
上更新 Label
。
你可能想要一个 Button
可以停止的地方 and/or 重置所有三个 Stopwatches
。这意味着在所有三个 RadioButtons
上将 Checked
设置为 False
,然后在所有三个 Stopwatches
上调用 Reset
。您可能也想 clear/reset Labels
。
像这样使用 RadioButtons
也存在潜在问题。如果您的 RadioButtons
之一在 Tab 键顺序中排在第一位,那么在您加载表单时它将默认获得焦点。聚焦 RadioButton
将检查它,因此这意味着您将默认启动 Stopwatch
。如果这不是您想要的,请确保其他控件位于 Tab 键顺序中的第一个。如果由于某种原因你不能这样做,处理表单的 Shown
事件,将 ActiveControl
设置为 Nothing
,取消选中 RadioButton
并重置相应的 Stopwatch
和 Label
.
作为最后一条一般信息,请注意我已经为所有内容命名,这样即使事先不了解该项目的人也不会怀疑所有内容是什么以及它们的用途。 SW
、SW1
和 SW2
这样的名字是不好的。即使您意识到 SW
意味着 Stopwatch
,您也不知道每个值的实际用途。在 Intellisense 的今天,它只是懒惰地使用这样的名称。每个有经验的开发人员都可以告诉你一个故事,关于一段时间后回过头来阅读他们自己的代码,却不知道他们所说的各种东西的意思。不要陷入那个陷阱,确保你早日养成良好的习惯。
编辑:
作为奖励,您可以通过以下方式正确使用通用事件处理程序。首先,定义一个自定义 Stopwatch
class 具有关联的 Label
:
Public Class StopwatchEx
Inherits Stopwatch
Public Property Label As Label
End Class
一旦建立关联,您就会自动知道使用哪个 Label
来显示 Stopwatch
的经过时间。接下来,定义一个具有关联 Stopwatch
:
RadioButton
class
Public Class RadioButtonEx
Inherits RadioButton
Public Property Stopwatch As StopwatchEx
End Class
接下来,在您的表单上使用自定义 class 而不是标准 RadioButtons
。您可以直接从工具箱添加它们(您的自定义控件将在构建项目后自动添加),或者您可以编辑设计器代码文件并更改代码中控件的类型。后一种选择存在一定的风险,因此请务必事先创建备份。完成后,更改 Stopwatches
的类型并处理表单的 Load
事件以创建关联:
Private ReadOnly driveStopwatch As New StopwatchEx
Private ReadOnly waitStopwatch As New StopwatchEx
Private ReadOnly walkStopwatch As New StopwatchEx
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Associate Stopwatches with RadioButtons
driveRadioButton.Stopwatch = driveStopwatch
waitRadioButton.Stopwatch = waitStopwatch
walkRadioButton.Stopwatch = walkStopwatch
'Associate Labels with Stopwatches
driveStopwatch.Label = driveLabel
waitStopwatch.Label = waitLabel
walkStopwatch.Label = walkLabel
End Sub
您现在可以使用一个方法来处理所有三个 CheckedChanged
事件 RadioButtons
因为您现在可以对所有三个执行完全相同的操作:
Private Sub RadioButtons_CheckedChanged(sender As Object, e As EventArgs) Handles driveRadioButton.CheckedChanged,
waitRadioButton.CheckedChanged,
walkRadioButton.CheckedChanged
Dim rb = DirectCast(sender, RadioButtonEx)
Dim sw = rb.Stopwatch
If rb.Checked Then
sw.Start()
Else
sw.Stop()
sw.Label.Text = sw.Elapsed.ToString("hh\:mm\:ss")
End If
End Sub
引发事件的 RadioButton
会告诉您要使用哪个 Stopwatch
,那个会告诉您要使用哪个 Label
,因此无需为每个事件编写不同的代码。
Timer
的Tick
事件处理程序也可以用通用代码处理每个Stopwatch
:
Private Sub displayTimer_Tick(sender As Object, e As EventArgs) Handles displayTimer.Tick
For Each sw In {driveStopwatch, waitStopwatch, walkStopwatch}
If sw.IsRunning Then
sw.Label.Text = sw.Elapsed.ToString("hh\:mm\:ss")
Exit For
End If
Next
End Sub
您可以在 class 级别创建数组,但是,由于它只在这个地方使用,因此在这里创建它是有意义的。性能影响微不足道,它通过在使用它们的地方创建东西使代码更具可读性。
请注意,我在这段代码中确实使用了变量名的缩写。这是出于两个原因。首先,它们是在不同时间引用不同对象的变量。这意味着不可能使用特定于对象用途的名称。您可以使用基于上下文的名称,例如currentRadioButton
,但由于第二个原因,我在这里不这样做。
第二个原因是它们是在非常有限的范围内使用的局部变量。 rb
和 sw
变量的使用不超过声明它们的几行,因此很难不理解它们是什么。如果你这样命名一个字段,那么当你在代码中看到它时,你必须去别处寻找它是什么。在这段代码中,如果您正在查看其中一个变量的用法,那么声明也在视线范围内,因此您必须视而不见才能看不到您正在处理的是什么类型。基本上,如果一个变量的使用距离它的声明很远,那么我建议使用一个有意义的、描述性的名称。如果它只在其声明的几行内使用,那么一个简短的名称就可以了。我通常倾向于使用类型的首字母,就像我在这里所做的那样。如果您需要该类型的多个局部变量,我通常更喜欢使用描述性名称来消除它们的歧义,而不是使用数字。但是,有时确实没有特定目的的方法来做到这一点,在这种情况下,数字是可以的,例如在没有上下文的情况下比较两个 Strings
可能会使用 s1
和 s2
作为变量名称。