首先,我建议将税率计算提取到它自己的方法中。这将使事情保持清洁并启用该方法的早期返回,从而允许我们删除一堆else
语句。只要我们按顺序检查范围(升序或降序),我们就不需要检查两个边界(即>= x && <= y
)。这也使方法可测试,这是您应该始终考虑的 - 按钮单击的事件处理程序不是非常可测试的。*
其次,我建议您decimal
用于货币计算而不是 double
. 有关原因的更多信息,请参阅此答案(由 Jon Skeet 撰写)和此问题(它是重复的)。但是我会注意到,由于这看起来像是一个学习练习,double
应该没问题。但是请记住,您通常不希望将它们用于货币计算。
让我们定义我们的方法。它将接受status
和我们的income
. 它将验证状态是一个有效值(而不是说,Head Of Household
),否则抛出异常。然后我们可以按顺序处理每个范围,return
而不需要嵌套else
语句:
public static decimal GetTaxRate(string status, decimal income)
{
// we need to validate we have an appropriate status
var comp = StringComparison.OrdinalIgnoreCase;
if (!string.Equals(status, "married", comp) && !string.Equals(status, "single", comp))
throw new InvalidOperationException("Invalid Status");
if (string.Equals(status, "single", comp))
{
// note lack of "else" since we early return
// note lack of " && income >= " which is implied
if (income <= 9725) return 0.1m;
if (income <= 37650) return 0.15m;
if (income <= 91150) return 0.25m;
if (income <= 190150) return 0.28m;
if (income <= 413350) return 0.33m;
if (income <= 415050) return 0.35m;
}
else // married
{
if (income <= 18550) return 0.1m;
if (income <= 75300) return 0.15m;
if (income <= 151900) return 0.25m;
if (income <= 231450) return 0.28m;
if (income <= 413350) return 0.33m;
if (income <= 415050) return 0.35m;
}
// default for both
return 0.396m;
}
现在我们可以从您的按钮单击事件处理程序中调用此函数:
string status = "single"; // get this some how
string incomeAsString = "1234.56"; // get this value some how
// validate that the income is a valid value
decimal income;
if (!decimal.TryParse(incomeAsString, out income))
{
lblresult.Text = "Entered income is not a valid decimal value";
return;
}
decimal taxRate = GetTaxRate(status, income); // calculate tax rate
decimal totalTax = taxRate * income;
lblresult.Text = string.Format("Your tax rate is {0:P2} with taxes owed of {1:C2} (on income of {2:C2})", taxRate, totalTax, income);
我建议寻找对变量进行去字符串化的方法。status
您可以使用 anenum
来表示不同的状态。如果您只计划拥有这两个选项,您甚至可以使用 a bool isSingle
(尽管我不喜欢该选项)。
如果你想使用一些 C#8 特性,我们可以使用递归模式匹配switch
语句。我还在这里使用 C#7数字分隔符( _
) 使数字更易于阅读:
// warning C# 8.0+
public static decimal GetTaxRate(string status, decimal income)
{
// note: validation elided
switch (status.ToLower(), income)
{
case ("single", decimal s) when s <= 9_725:
case ("married", decimal m) when m <= 18_550:
return 0.1M;
case ("single", decimal s) when s <= 37_650:
case ("married", decimal m) when m <= 75_300:
return 0.15M;
case ("single", decimal s) when s <= 91_150:
case ("married", decimal m) when m <= 151_900:
return 0.25m;
case ("single", decimal s) when s <= 190_150:
case ("married", decimal m) when m <= 231_450:
return 0.28m;
case (_, decimal i) when i <= 413_350:
return 0.33m;
case (_, decimal i) when i <= 415_050:
return 0.35m;
default:
return 0.396m;
}
}
在 C#9中,我们将能够在表达式中使用关系模式switch
(参见SharpLab 示例)
// warning C# 9.0
public static decimal GetTaxRate(string status, decimal income)
{
// note: validation elided
if (string.Equals(status, "single", StringComparison.OrdinalIgnoreCase))
{
return income switch {
<= 9725 => 0.1M,
<= 37650 => 0.15M,
<= 91150 => 0.25M,
<= 190150 => 0.28M,
<= 413350 => 0.33M,
<= 415050 => 0.35M,
_ => 0.396M // default
};
}
// married
return income switch {
<= 18550 => 0.1M,
<= 75300 => 0.15M,
<= 151900 => 0.25M,
<= 231450 => 0.28M,
<= 413350 => 0.33M,
<= 415050 => 0.35M,
_ => 0.396M // default
};
}
注意:m
andM
是用于指定decimal
文字的后缀。外壳无关紧要——它们是可以互换的
* 可以手动测试和验证事件处理程序(有人盯着标签文本),但理想情况下,您希望能够使用极点 xUnit 工具来定义自动化案例。