对我的密码有什么评论吗?
public class LoanRepaymentPlan
{
public decimal TotalRepaymentAmount { get; private set; }
public decimal CurrentOutstandingAmount { get; private set; }
public decimal MonthlyRepaymentInterestAmount { get; private set; }
public decimal MonthlyRepaymentAmount { get; private set; }
public DateTime RepaymentDate { get; private set; }
private LoanRepaymentPlan(decimal TotalRepaymentAmount, decimal CurrentOutstandingAmount, decimal MonthlyRepaymentAmount)
{
this.TotalRepaymentAmount = TotalRepaymentAmount;
this.CurrentOutstandingAmount = CurrentOutstandingAmount;
this.MonthlyRepaymentAmount = MonthlyRepaymentAmount;
}
public static LoanRepaymentPlan Create(decimal TotalRepaymentAmount, decimal CurrentOutstandingAmount, decimal MonthlyRepaymentAmount)
{
return new LoanRepaymentPlan(TotalRepaymentAmount, CurrentOutstandingAmount, MonthlyRepaymentAmount);
}
}static void Main(string[] args)
{
Console.WriteLine("Equated Monthly Installment (EMI) for Home Loan");
Console.WriteLine("Total Loan Amount: ");
var principal = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("Interest Rate Per Annum (%): ");
var interestRatePerAnnum = Convert.ToDouble(Console.ReadLine()) / 100;
Console.WriteLine("Loan Period (in years): ");
var years = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("------------------------------------------------");
Console.WriteLine("Monthly installment: " + HousingLoanInterest(principal, interestRatePerAnnum, years).ToString("C2"));
decimal totalRepaymentAmount = 0;
var monthlyRepaymentAmount = HousingLoanInterest(principal, interestRatePerAnnum, years);
var tenure = years * 12;
var principalAndInterest = CompoundInterest(principal, interestRatePerAnnum, 12, years);
List<LoanRepaymentPlan> repaymentPlans = new List<LoanRepaymentPlan>();
while (tenure >= 0)
{
totalRepaymentAmount = totalRepaymentAmount + (decimal)monthlyRepaymentAmount;
tenure = tenure - 1;
repaymentPlans.Add(LoanRepaymentPlan.Create(totalRepaymentAmount,
(decimal)principalAndInterest - totalRepaymentAmount, (decimal)monthlyRepaymentAmount));
}
var table = new ConsoleTable("Current Outstanding Amount", "Monthly Repayment Amount", "Total Repayment Amount");
foreach (var item in repaymentPlans)
{
table.AddRow(item.CurrentOutstandingAmount, item.MonthlyRepaymentAmount, item.TotalRepaymentAmount);
}
table.Write();
}static double CompoundInterest(double principal, double interestRate, int timesPerYear, double years)
{
// (1 + r/n)
double body = 1 + (interestRate / timesPerYear);
// nt
double exponent = timesPerYear * years;
// P(1 + r/n)^nt
return principal * Math.Pow(body, exponent);
}
static double HousingLoanInterest(double loanAmount, double interestRate, double years)
{
return (loanAmount * Math.Pow((interestRate / 12) + 1,
(years * 12)) * interestRate / 12) / (Math.Pow
(interestRate / 12 + 1, (years * 12)) - 1);
}这一计算有些不准确,但我以后会设法弄清楚的。困扰我的一件事是Math.Pow,它使用double。我已尝试将计算转换为decimal,但目前无法这样做。
发布于 2020-07-14 03:06:16
var特别是考虑到您有准确性问题,对于您自己理解代码在以下声明中所做的事情非常重要:
var monthlyRepaymentAmount = HousingLoanInterest(principal, interestRatePerAnnum, years);
var tenure = years * 12;
var principalAndInterest = CompoundInterest(principal, interestRatePerAnnum, 12, years);仅从这些行,我就不知道它们是否应该是双数,整数,小数等等,只需拼出实际的类型。
totalRepaymentAmount = totalRepaymentAmount + (decimal)monthlyRepaymentAmount;
tenure = tenure - 1;可以是
totalRepaymentAmount += monthlyRepaymentAmount;
tenure--;这可以从一些临时变量中受益,但即使没有这些变量,
return (loanAmount * Math.Pow((interestRate / 12) + 1,
(years * 12)) * interestRate / 12) / (Math.Pow
(interestRate / 12 + 1, (years * 12)) - 1);更易读懂
return (
loanAmount * Math.Pow(
interestRate / 12 + 1,
years * 12
) * interestRate / 12
) / (
Math.Pow(
interestRate / 12 + 1,
years * 12
) - 1
);而这又相当于
double annual = interestRate / 12;
double pow = Math.Pow(
annual + 1,
years * 12
);
return loanAmount * pow * annual / (pow - 1);更简单:
double annual = interestRate / 12,
pow = Math.Pow(
annual + 1,
-12 * years
);
return loanAmount * annual / (1 - pow);请测试这个是否等价。
发布于 2020-07-14 08:42:04
@Reinderien提到了一些很大的改进点,所以我不会重复它们。
还有其他领域可以微调。
在这种情况下,您不需要显式地为属性指定private set。如果您只指定getter,那将是非常好的,因为ctors可以设置属性,即使它们不公开getter。
public class LoanRepaymentPlan
{
public decimal TotalRepaymentAmount { get; }
public decimal CurrentOutstandingAmount { get; }
public decimal MonthlyRepaymentInterestAmount { get; }
public decimal MonthlyRepaymentAmount { get; }
public DateTime RepaymentDate { get; }
public LoanRepaymentPlan(decimal TotalRepaymentAmount, decimal CurrentOutstandingAmount, decimal MonthlyRepaymentAmount)
{
this.TotalRepaymentAmount = TotalRepaymentAmount;
this.CurrentOutstandingAmount = CurrentOutstandingAmount;
this.MonthlyRepaymentAmount = MonthlyRepaymentAmount;
}
}使用这种方法,您已经创建了一个不可变的类(在初始化之后不能修改)。从内存优化的角度来看,将class更改为struct是有意义的。这样,它很有可能被分配到堆栈上(甚至在CPU寄存器中),因此不需要为这个对象分配堆,也不需要gargabe集合。
还有另一个有趣的话题,目前还没有,但值得一提的是C# 9记录。在C# 9中,可以通过以下代码实现相同的目标:
public data class LoanRepaymentPlan
{
public decimal TotalRepaymentAmount { get; init; }
public decimal CurrentOutstandingAmount { get; init; }
public decimal MonthlyRepaymentInterestAmount { get; }
public decimal MonthlyRepaymentAmount { get; init; }
public DateTime RepaymentDate { get; }
}在我看来,这没有多大意义。您必须在每次初始化时多写两个字符,并且必须维护两个对象创建者函数。我不明白这个静电发生器的全部意义。
您的代码在双小数类型之间跳来跳去。如果精度很重要,那么在任何地方都使用decimal,否则就使用double。
Convert.ToDouble(Console.ReadLine())确实容易出错。如果用户输入“一些字符串”怎么办?它会抛出一个FormatException。使用double.TryParse代替:
var userInput = Console.ReadLine();
if(!double.TryParse(userInput, out var principal))
{
//TODO: handle that case
}您还应该考虑根据有效范围验证用户输入。例如,利率应该在0到100之间。
尽管在我看来,while循环工作得很好,但for循环可能更有表现力和简洁性。
for (var tenure = years * 12; tenure >= 0; tenure--)
{
totalRepaymentAmount += (decimal)monthlyRepaymentAmount;
repaymentPlans.Add(LoanRepaymentPlan.Create(totalRepaymentAmount,
(decimal)principalAndInterest - totalRepaymentAmount, (decimal)monthlyRepaymentAmount));
}迭代器变量在被修改的地方声明。
它提高了可读性和可维护性。
我强烈鼓励你把你的主要职能分成几个小块,专门负责:
它还将提高可测试性、可维护性和可读性。
https://codereview.stackexchange.com/questions/245412
复制相似问题