C#模拟银行ATM系统源码与逻辑解析
本文详细介绍了一个使用 C# 编写的银行 ATM 系统模拟项目。通过定义抽象基类 Account 及其子类 SavingAccount 和 CreditAccount,实现了不同账户类型的差异化取款逻辑;利用 Bank 类统一管理账户生命周期与资金流水;结合 Program 类的控制台交互,完整演示了从用户开户、身份验证到存款、取款及额度管理的业务闭环。
结果或目标
本示例旨在通过控制台应用程序模拟真实银行的业务逻辑,展示 C# 在面向对象编程(OOP)中的核心特性应用。主要实现的功能包括:
- 多态性:不同账户类型对
Withdraw方法有不同的行为实现。 - 封装性:银行账户的核心状态(如密码、余额)由内部逻辑保护,通过受控方法访问。
- 集合管理:
Bank类作为中央管理器,统一处理所有账户的注册、查找与统计。 - 业务流程:覆盖开户、登录认证、存取款校验、信用额度设置等典型金融场景。
准备条件
开发此项目需要基础的 C# 编程环境:
- 操作系统:Windows / Linux / macOS(.NET 兼容环境)。
- 开发工具:Visual Studio, VS Code 或 JetBrains Rider。
- 运行时:.NET Framework 4.x 或 .NET Core / .NET 5+。
- 输入方式:标准输入输出(Console.ReadLine / Console.WriteLine)。
真实操作顺序
第1步:构建抽象账户基类 Account
目的:提取所有账户共有的属性与方法,定义规范。
代码实现:
using System;
namespace ATM
{
abstract class Account
{
// 基本属性
protected long id; // 账户号码
protected string password; // 密码
protected string name; // 姓名
protected string personId; // 身份证
protected string email; // 邮箱
protected double balance; // 余额
public long ID { get { return id; } set { id = value; } }
public string PassWord { get { return password; } set { password = value; } }
public string Name { get { return name; } set { name = value; } }
public string PersonId { get { return personId; } set { personId = value; } }
public string Email { get { return email; } set { email = value; } }
public double Balance { get { return balance; } set { balance = value; } }
private static long idBuilder = 100000;
public static long IdBuilder { get { return idBuilder; } set { idBuilder = value; } }
// 公共存款方法
public void Deposit(double sum)
{
if (sum < 0)
throw new InvalidOperationException("输入的金额为负数");
balance += sum;
}
// 抽象取款方法,由子类实现具体逻辑
public abstract void Withdraw(double sum);
public Account() { }
public Account(string password, string name, string personId, string email)
{
this.id = ++idBuilder;
this.password = password;
this.name = name;
this.personId = personId;
this.email = email;
}
}
}
第2步:实现信用账户 CreditAccount
目的:实现允许透支特性的子类,重写取款逻辑以校验是否超出(余额+额度)。
入口/动作:继承 Account,添加 ceiling 属性。
class CreditAccount : Account
{
protected double ceiling; // 透支额度
public double Ceiling
{
get { return ceiling; }
set { ceiling = value; }
}
public CreditAccount(string password, string name, string personId, string email)
: base(password, name, personId, email) { }
public override void Withdraw(double sum)
{
if (sum < 0)
throw new InvalidOperationException("输入的金额为负数!");
// 关键逻辑:总额不能超过 余额 + 信用额度
if (sum > balance + ceiling)
throw new InvalidOperationException("金额已经超出余额和透支度的总数了");
balance -= sum;
}
}
第3步:实现储蓄账户 SavingAccount
目的:实现普通账户逻辑,不支持透支,取款金额不得超过当前余额。
class SavingAccount : Account
{
public SavingAccount(string password, string name, string personId, string email)
: base(password, name, personId, email) { }
public override void Withdraw(double sum)
{
if (sum < 0)
throw new InvalidOperationException("输入的金额为负数!");
// 关键逻辑:不能透支
if (sum > balance)
throw new InvalidOperationException("金额已经超出金额!");
balance -= sum;
}
}
第4步:构建银行管理中心 Bank 类
目的:管理所有账户对象的集合,提供开户、查找、存取款及统计接口。
重要参数:List<Account> 存储所有账户,支持多态调用。
class Bank
{
private List<Account> accounts;
public List<Account> Accounts
{
get { return accounts; }
set { accounts = value; }
}
private int currentAccountNumber;
public int CurrentAccountNumber
{
get { return currentAccountNumber; }
set { currentAccountNumber = value; }
}
public Bank()
{
accounts = new List<Account>();
}
// 开户逻辑
public Account OpenAccount(string password, string confirmationPassword, string name, string personId, string email, int typeOfAccount)
{
Account newAccount;
if (!password.Equals(confirmationPassword))
throw new InvalidOperationException("两次密码输入的不一致");
switch (typeOfAccount)
{
case 1: newAccount = new SavingAccount(password, name, personId, email); break;
case 2: newAccount = new CreditAccount(password, name, personId, email); break;
default: throw new ArgumentOutOfRangeException("账户类型是1和2之间的整数");
}
accounts.Add(newAccount);
return newAccount;
}
// 私有辅助方法:根据ID获取账户
private Account GetAccountByID(long id)
{
foreach (Account account in accounts)
{
if (account.ID == id)
return account;
}
return null;
}
// 登录验证
public Account SignIn(long id, string password)
{
foreach (Account account in accounts)
{
if (account.ID == id && account.PassWord.Equals(password))
return account;
}
throw new InvalidOperationException("用户名或者密码不正确,请重试");
}
// 存款
public Account Deposit(long id, double sum)
{
Account account = GetAccountByID(id);
if (account != null)
{
account.Deposit(sum);
return account;
}
throw new InvalidOperationException("非法账户!");
}
// 取款
public Account Withdraw(long id, double sum)
{
Account account = GetAccountByID(id);
if (account != null)
{
account.Withdraw(sum);
return account;
}
throw new InvalidOperationException("非法账户!");
}
// 设置信用额度
public Account SetCeiling(long id, double newCeiling)
{
Account account = GetAccountByID(id);
try
{
// 尝试转换为CreditAccount,失败则说明是普通储蓄账户
(account as CreditAccount).Ceiling = newCeiling;
return account;
}
catch (Exception)
{
throw new InvalidOperationException("次账户不是信用账户!");
}
throw new InvalidOperationException("非法账户");
}
// 统计总余额
public double GetTotalBalance()
{
double totalBalance = 0;
foreach (Account account in accounts)
{
totalBalance += account.Balance;
}
return totalBalance;
}
// 统计总信用额度
public double GetTotalCeiling()
{
double totalCeiling = 0;
foreach (Account account in accounts)
{
if (account is CreditAccount)
{
totalCeiling += (account as CreditAccount).Ceiling;
}
}
return totalCeiling;
}
}
第5步:实现控制台交互 Program
目的:提供用户界面,循环接收指令并调度 Bank 类的方法。
关键流程:
- 初始化:创建
Bank icbc实例。 - 菜单循环:使用
switch-case监听数字键选择功能。 - 开户:校验姓名、身份证长度(18位)、密码一致性,区分账户类型存入银行。
- 交易:需先通过
SignIn获取账户实体,再进行存取款操作。
class Program
{
static Account SignIn(Bank icbc)
{
Console.WriteLine("\nPlease input your account ID");
long id;
try
{
id = long.Parse(Console.ReadLine());
}
catch (FormatException)
{
Console.WriteLine("Invalid account ID!");
return null;
}
Console.WriteLine("Please input your password");
string password = Console.ReadLine();
Account account;
try
{
account = icbc.SignIn(id, password);
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.Message);
return null;
}
return account;
}
static void Main(string[] args)
{
Bank icbc = new Bank();
while (true)
{
Console.WriteLine("Please choose the service your need");
Console.WriteLine("(1) Open a new account");
Console.WriteLine("(2) Desposit");
Console.WriteLine("(3) Withdraw");
Console.WriteLine("(4) Set Ceiling");
Console.WriteLine("(5) Get Total Balance");
Console.WriteLine("(6) Get Total Ceiling");
Console.WriteLine("(0) Exit");
string choice;
choice = Console.ReadKey().KeyChar.ToString();
switch (choice)
{
case "1":
{
// 开户逻辑...
// ...省略部分控制台输入代码保持一致性...
// 实际运行时需要补全原代码中case 1内读取name, email, password等细节
break;
}
case "2":
{
Account account = SignIn(icbc);
if (account == null) break;
int amount;
while (true)
{
Console.WriteLine("Please input the amount;");
try { amount = int.Parse(Console.ReadLine()); break; }
catch { Console.WriteLine("Invalid input!"); continue; }
}
try
{
icbc.Deposit(account.ID, amount);
Console.WriteLine("Deposit successfully,our balance is{0}元",account.Balance);
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.Message);
}
break;
}
case "0": return;
default: Console.WriteLine("\nInvalid option,plwase choose again!"); break;
}
}
}
}
完成判断
程序成功编译且无语法错误;控制台启动后能显示主菜单;正确输入ID和密码可登录并执行存取款;新开信用账户后可成功设置 Ceiling;查询 GetTotalBalance 时数值实时刷新。若涉及数据库持久化,目前仅存储在内存 List<Account> 中,重启即丢失。
原文有依据的排错
类型转换异常:在 SetCeiling 方法中,如果传入的 id 对应的账户是 SavingAccount,强制转换为 CreditAccount 会导致异常。代码通过 try-catch 捕获该异常并抛出友好的 "次账户不是信用账户!" 提示。
输入格式错误:在输入金额或ID时,使用了 int.Parse 或 long.Parse,包裹在 try-catch 中以防止非数字字符导致程序崩溃。
简要总结
这是一个经典的面向对象练习案例。通过 Account 基类规范接口,利用 SavingAccount 和 CreditAccount 展现多态差异,Bank 类集中管理状态,清晰展示了封装、继承和多态在构建小型业务系统时的协作关系。以上就是C#模拟银行ATM系统的详细内容,更多关于C#面向对象编程的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













