Visual Basic 中如何计算用户的总付款金额?
支付小周
结论
代码没有输出,原因主要有两个:程序没有累计 5 次输入,也没有更新 hourCounter 和 hourTotal。另外,原来的条件只在数值正好等于 15、25 或 40 时才会成立。总时数如果是 20 或 35,就不会进入任何分支。
可以把输入次数和累计时数定义为窗体级变量。每次单击按钮后,程序按以下顺序处理:
- 将文本框内容转换为数值;
- 检查时数是否有效且不小于零;
- 检查累计时数是否会超过 40;
- 累加时数,并将输入次数加 1;
- 完成第 5 次输入后,计算并显示付款金额。
为什么原代码没有得到结果
没有累计输入值
现有代码缺少下面这样的累加操作:
hourTotal += enteredHours
hourCounter += 1
所以程序既没有记录用户输入了多少次,也没有计算 5 次输入的总时数。
相等判断过于严格
下面的条件只会在文本框内容正好为 15 时执行:
If Enterhourstextbox.Text = 15 Then
另外两个条件也只接受 hourCounter 正好等于 25 或 40:
If hourCounter = 25 Then
If hourCounter = 40 Then
如果 hourCounter 表示输入次数,它的取值应该是 1 到 5,不能用来和工时阈值比较。如果它表示累计工时,这个变量名很容易让人误解,而且仍然不能只判断三个精确值。
文本没有显式转换为数字
Enterhourstextbox.Text 的类型是 String。直接将它与数字比较,需要依赖隐式类型转换。启用 Option Strict On 后,这种写法无法通过编译。
更稳妥的方式是使用 Decimal.TryParse:
Dim enteredHours As Decimal
If Not Decimal.TryParse(Enterhourstextbox.Text, enteredHours) Then
MessageBox.Show("请输入有效的工作时数。")
Return
End If
变量需要在多次单击之间保留
如果在按钮事件内部定义 hourTotal 和输入次数,每次单击按钮时,这两个变量都会重新初始化。应当把它们定义在窗体类中:
Private hourTotal As Decimal = 0D
Private entryCount As Integer = 0
完整实现示例
下面的代码采用这套付款规则:
- 总时数不超过 15 小时:每小时 9;
- 总时数超过 15、但不超过 25 小时:每小时 13;
- 总时数超过 25、但不超过 40 小时:每小时 20。
最终总时数会决定一个统一的小时费率。
Option Strict On
Public Class TimeEntry
Private Const MaximumEntries As Integer = 5
Private Const MaximumHours As Decimal = 40D
Private hourTotal As Decimal = 0D
Private entryCount As Integer = 0
Private Sub SubmitTimeButton_Click(
sender As Object,
e As EventArgs
) Handles SubmitTimeButton.Click
Dim enteredHours As Decimal
' 将文本转换为数值
If Not Decimal.TryParse(Enterhourstextbox.Text, enteredHours) Then
MessageBox.Show("请输入有效的工作时数。")
Enterhourstextbox.SelectAll()
Enterhourstextbox.Focus()
Return
End If
' 不接受负数
If enteredHours < 0D Then
MessageBox.Show("工作时数不能为负数。")
Enterhourstextbox.SelectAll()
Enterhourstextbox.Focus()
Return
End If
' 防止输入超过 5 次
If entryCount >= MaximumEntries Then
MessageBox.Show("已经完成 5 次工时输入。")
Return
End If
' 检查累计时数是否超过 40
If hourTotal + enteredHours > MaximumHours Then
Dim remainingHours As Decimal = MaximumHours - hourTotal
MessageBox.Show(
$"累计工作时数不能超过 40。当前最多还可以输入 {remainingHours} 小时。"
)
Enterhourstextbox.SelectAll()
Enterhourstextbox.Focus()
Return
End If
' 保存本次输入
hourTotal += enteredHours
entryCount += 1
TimeRecordsListBox.Items.Add(
$"第 {entryCount} 次:{enteredHours} 小时"
)
Enterhourstextbox.Clear()
Enterhourstextbox.Focus()
' 收集完 5 次输入后计算付款
If entryCount = MaximumEntries Then
Dim hourlyRate As Decimal
If hourTotal <= 15D Then
hourlyRate = 9D
ElseIf hourTotal <= 25D Then
hourlyRate = 13D
Else
hourlyRate = 20D
End If
Dim payment As Decimal = hourTotal * hourlyRate
getpaymentlabel.Text =
$"总时数:{hourTotal};付款金额:{payment:C2}"
SubmitTimeButton.Enabled = False
Else
getpaymentlabel.Text =
$"已输入 {entryCount}/5 次,当前累计 {hourTotal} 小时。"
End If
End Sub
Private Sub CleartimeButton_Click(
sender As Object,
e As EventArgs
) Handles CleartimeButton.Click
TimeRecordsListBox.Items.Clear()
getpaymentlabel.Text = String.Empty
Enterhourstextbox.Clear()
' 同时重置内部状态
hourTotal = 0D
entryCount = 0
SubmitTimeButton.Enabled = True
Enterhourstextbox.Focus()
End Sub
End Class
如果费率采用分段计算
付款规则还需要进一步确认。上面的代码会根据最终总时数选择一个统一费率。例如,总时数为 30 时,付款金额为:
30 × 20
如果规则是前 15 小时按 9 计算,接下来的 10 小时按 13 计算,超过 25 小时的部分按 20 计算,就需要采用分段计费。可以把计算过程单独写成一个函数:
Private Function CalculatePayment(totalHours As Decimal) As Decimal
If totalHours <= 15D Then
Return totalHours * 9D
ElseIf totalHours <= 25D Then
Return (15D * 9D) +
((totalHours - 15D) * 13D)
Else
Return (15D * 9D) +
(10D * 13D) +
((totalHours - 25D) * 20D)
End If
End Function
完成第 5 次输入后,再调用这个函数:
Dim payment As Decimal = CalculatePayment(hourTotal)
getpaymentlabel.Text =
$"总时数:{hourTotal};付款金额:{payment:C2}"
注意事项
hourTotal保存累计工时,entryCount保存输入次数,不能混用。- 清空界面时,也要重置这两个变量。只清空控件不会改变程序内部保存的状态。
- 使用
Decimal保存工时和金额,可以处理7.5这类带小数的时数。 payment:C2会根据当前系统的区域设置显示货币符号。如果需要显示特定货币,应指定相应格式或直接添加货币单位。- 建议启用
Option Strict On,以便尽早发现字符串与数字之间的错误比较及隐式转换。
备注:内容仅供参考。
