首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 打造個人住房貸款計算機:等本息/等本金試算表完整實作 (XML 設定 + 物件導向設計)

     編輯:關於C#

貸款試算是經典財務應用場景。本文展示一個完整的 C# WinForms 貸款計算機,支援等本息 (固定月繳)與等本金 (遞減月繳)兩種還款方式、多種貸款利率方案 (公積金/商業貸款/優惠利率)、利率隨時間區段調整,採用 XML 設定檔 外部化利率參數,核心計算邏輯以抽象基類 + 策略模式封裝,介面與邏輯分離。

架構概覽

  • LoanCalculator.xml:利率參數、預設值外部化
  • Config.cs:XML 反序列化為強型別設定物件
  • LoanBase / LoanEq / LoanDesc:Template Method 模式實作兩種試算演算法
  • MainForm.cs:UI 綁定、參數收集、結果顯示

1. XML 設定檔 (LoanCalculator.xml)




  

low:5年期(含)以下利率,high:5年期以上利率,依貸款總期數自動判斷。

2. 核心計算邏輯 (Template Method)


// 抽象基類
public abstract class LoanBase
{
    public DataTable Schedule { get; private set; }

    protected LoanBase(decimal principal, int months, DateTime startDate, RateSegment[] rates)
    {
        Schedule = BuildScheduleTable();
        Calculate(principal, months, startDate, rates);
    }

    protected abstract void Calculate(decimal principal, int months, DateTime startDate, RateSegment[] rates);

    protected decimal GetMonthlyRate(DateTime date, int totalMonths, RateSegment[] rates)
    {
        // 找適用利率區段 (依日期遞減查找)
        for (int i = rates.Length - 1; i >= 0; i--)
            if (date >= rates[i].EffectiveDate)
                return (totalMonths <= 60 ? rates[i].RateLow : rates[i].RateHigh) / 100m / 12m;
        return 0m;
    }

    protected decimal Round(decimal v) => Math.Round(v, 2, MidpointRounding.AwayFromZero);

    private DataTable BuildScheduleTable()
    {
        var dt = new DataTable();
        dt.Columns.Add("期數", typeof(int));
        dt.Columns.Add("還款日期", typeof(DateTime));
        dt.Columns.Add("本金", typeof(decimal));
        dt.Columns.Add("利息", typeof(decimal));
        dt.Columns.Add("月繳", typeof(decimal));
        dt.Columns.Add("剩餘本金", typeof(decimal));
        dt.Columns.Add("累計本金", typeof(decimal));
        dt.Columns.Add("累計利息", typeof(decimal));
        return dt;
    }
}

// 等本息:月繳固定
public sealed class LoanEq : LoanBase
{
    public LoanEq(decimal p, int m, DateTime d, RateSegment[] r) : base(p, m, d, r) { }

    protected override void Calculate(decimal principal, int months, DateTime date, RateSegment[] rates)
    {
        decimal totalPrincipal = 0, totalInterest = 0;
        decimal fixedMonthlyPayment = -1, currentRate = -1;

        for (int m = months; m >= 1; m--, date = date.AddMonths(1))
        {
            decimal rate = GetMonthlyRate(date, months, rates);
            decimal interest = Round(principal * rate);

            if (rate != currentRate) // 利率變動重新計算固定月繳
            {
                currentRate = rate;
                fixedMonthlyPayment = GetFixedPayment(principal, currentRate, m);
            }

            decimal principalPaid = fixedMonthlyPayment - interest;
            principal -= principalPaid;

            // 最後一期修正殘差
            if (m == 1 && principal != 0)
            {
                principalPaid += principal;
                interest -= principal;
                principal = 0;
            }

            totalPrincipal += principalPaid;
            totalInterest += interest;
            Schedule.Rows.Add(months - m + 1, date, principalPaid, interest, fixedMonthlyPayment, principal, totalPrincipal, totalInterest);
        }
    }

    private decimal GetFixedPayment(decimal p, decimal r, int n)
    {
        double factor = Math.Pow(1 + (double)r, n);
        return Round((decimal)((double)p * (double)r * factor / (factor - 1)));
    }
}

// 等本金:本金固定,月繳遞減
public sealed class LoanDesc : LoanBase
{
    public LoanDesc(decimal p, int m, DateTime d, RateSegment[] r) : base(p, m, d, r) { }

    protected override void Calculate(decimal principal, int months, DateTime date, RateSegment[] rates)
    {
        decimal fixedPrincipal = Round(principal / months);
        decimal totalPrincipal = 0, totalInterest = 0;

        for (int m = months; m >= 1; m--, date = date.AddMonths(1))
        {
            decimal rate = GetMonthlyRate(date, months, rates);
            decimal interest = Round(principal * rate);
            decimal monthlyPayment = fixedPrincipal + interest;
            principal -= fixedPrincipal;

            if (m == 1 && principal != 0)
            {
                fixedPrincipal += principal;
                monthlyPayment += principal;
                principal = 0;
            }

            totalPrincipal += fixedPrincipal;
            totalInterest += interest;
            Schedule.Rows.Add(months - m + 1, date, fixedPrincipal, interest, monthlyPayment, principal, totalPrincipal, totalInterest);
        }
    }
}

3. UI 整合 (MainForm 關鍵片段)


public partial class MainForm : Form
{
    private Config _cfg;

    private void MainForm_Load(object sender, EventArgs e)
    {
        _cfg = new Config("LoanCalculator.xml");
        txtPrincipal.Text = _cfg.Balance.ToString();       // 萬元
        txtMonths.Text = _cfg.Months.ToString();
        dtpStartDate.Value = _cfg.Date;
        rdoEq.Checked = _cfg.IsEqualPrincipalInterest;
        lstLoanType.DataSource = _cfg.Items;
        lstLoanType.SelectedItem = _cfg.Item;
    }

    private void lstLoanType_SelectedIndexChanged(object sender, EventArgs e)
    {
        _cfg.SetRates(lstLoanType.SelectedItem.ToString());
        BindRateGrid();
    }

    private void btnCalculate_Click(object sender, EventArgs e)
    {
        if (!decimal.TryParse(txtPrincipal.Text, out var principal) || principal <= 0) return;
        if (!int.TryParse(txtMonths.Text, out var months) || months <= 0) return;

        principal *= 10000m; // 萬元轉元
        var rates = _cfg.Rates;
        LoanBase loan = rdoEq.Checked
            ? new LoanEq(principal, months, dtpStartDate.Value, rates)
            : new LoanDesc(principal, months, dtpStartDate.Value, rates);

        dgvSchedule.DataSource = loan.Schedule;
        FormatGrid();
    }
}

關鍵設計亮點

  1. 參數外部化:利率政策調整只需改 XML,無需重新編譯。
  2. 策略模式:LoanEq/LoanDesc 共用基類邏輯,擴充新還款方式只需新增子類別。
  3. 利率區段自動匹配:依貸款日期與期數自動套用正確利率,支援歷史利率追溯。
  4. 金額精度控制:全程使用 decimal,MidpointRounding.AwayFromZero 四捨五入,最後一期修正殘差。
  5. 單位換算:UI 以「萬元」輸入,內部以「元」計算,避免小數點錯位。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved