本章目标
通过本章学习,你将掌握: - 面向对象编程的核心概念 - 类和对象的定义与使用 - 封装、继承、多态的实现 - 构造函数和析构函数 - 访问修饰符的使用 - 静态成员和实例成员 - 属性和索引器 - 方法重载和重写
1. 面向对象编程概述
1.1 什么是面向对象编程
面向对象编程(Object-Oriented Programming,OOP)是一种编程范式,它将现实世界中的事物抽象为对象,通过对象之间的交互来解决问题。
核心思想: - 抽象:提取事物的本质特征,忽略非本质特征 - 封装:将数据和操作数据的方法组合在一起 - 继承:子类可以继承父类的特性 - 多态:同一接口可以有不同的实现
1.2 面向对象的优势
// 面向过程的方式
public static void ProcessOrder()
{
// 验证订单
if (ValidateOrder())
{
// 计算价格
decimal price = CalculatePrice();
// 处理支付
if (ProcessPayment(price))
{
// 发送确认
SendConfirmation();
}
}
}
// 面向对象的方式
public class Order
{
public bool Validate() { /* 验证逻辑 */ return true; }
public decimal CalculatePrice() { /* 计算逻辑 */ return 100m; }
public bool ProcessPayment() { /* 支付逻辑 */ return true; }
public void SendConfirmation() { /* 确认逻辑 */ }
public void Process()
{
if (Validate())
{
decimal price = CalculatePrice();
if (ProcessPayment())
{
SendConfirmation();
}
}
}
}
优势: - 模块化:代码组织更清晰 - 可重用性:类可以在多个地方使用 - 可维护性:修改更容易,影响范围更小 - 可扩展性:通过继承和多态扩展功能
2. 类和对象
2.1 类的定义
类是对象的模板或蓝图,定义了对象的属性和行为。
// 基本类定义
public class Person
{
// 字段(Fields)
private string name;
private int age;
private string email;
// 属性(Properties)
public string Name
{
get { return name; }
set
{
if (!string.IsNullOrWhiteSpace(value))
name = value;
else
throw new ArgumentException("姓名不能为空");
}
}
public int Age
{
get { return age; }
set
{
if (value >= 0 && value <= 150)
age = value;
else
throw new ArgumentException("年龄必须在0-150之间");
}
}
// 自动属性(Auto-implemented Properties)
public string Email { get; set; }
// 构造函数(Constructor)
public Person()
{
name = "未知";
age = 0;
email = "";
}
public Person(string name, int age)
{
Name = name; // 使用属性设置,会进行验证
Age = age;
Email = "";
}
public Person(string name, int age, string email) : this(name, age)
{
Email = email;
}
// 方法(Methods)
public void Introduce()
{
Console.WriteLine($"你好,我是{Name},今年{Age}岁。");
}
public bool IsAdult()
{
return Age >= 18;
}
public void SendEmail(string subject, string body)
{
if (string.IsNullOrWhiteSpace(Email))
{
Console.WriteLine("邮箱地址为空,无法发送邮件");
return;
}
Console.WriteLine($"发送邮件到 {Email}");
Console.WriteLine($"主题: {subject}");
Console.WriteLine($"内容: {body}");
}
// 重写ToString方法
public override string ToString()
{
return $"Person: {Name}, Age: {Age}, Email: {Email}";
}
}
2.2 对象的创建和使用
public class PersonDemo
{
public static void Main()
{
Console.WriteLine("=== 对象创建和使用演示 ===");
// 使用不同的构造函数创建对象
Person person1 = new Person();
Person person2 = new Person("张三", 25);
Person person3 = new Person("李四", 30, "lisi@example.com");
// 设置属性
person1.Name = "王五";
person1.Age = 28;
person1.Email = "wangwu@example.com";
// 调用方法
person1.Introduce();
person2.Introduce();
person3.Introduce();
// 使用属性
Console.WriteLine($"\n{person2.Name} 是否成年: {person2.IsAdult()}");
// 发送邮件
person3.SendEmail("会议通知", "明天下午2点开会");
// 使用ToString
Console.WriteLine($"\n对象信息: {person1}");
// 对象比较
Person person4 = new Person("张三", 25);
Console.WriteLine($"\nperson2 == person4: {person2 == person4}"); // False,引用不同
Console.WriteLine($"person2.Name == person4.Name: {person2.Name == person4.Name}"); // True,值相同
}
}
2.3 访问修饰符
public class AccessModifierDemo
{
// public: 任何地方都可以访问
public string PublicField = "公共字段";
// private: 只能在当前类内部访问
private string privateField = "私有字段";
// protected: 当前类和子类可以访问
protected string protectedField = "受保护字段";
// internal: 同一程序集内可以访问
internal string internalField = "内部字段";
// protected internal: protected + internal
protected internal string protectedInternalField = "受保护内部字段";
// private protected: 同一程序集内的子类可以访问
private protected string privateProtectedField = "私有受保护字段";
public void DemonstrateAccess()
{
// 在类内部,所有成员都可以访问
Console.WriteLine(PublicField);
Console.WriteLine(privateField);
Console.WriteLine(protectedField);
Console.WriteLine(internalField);
Console.WriteLine(protectedInternalField);
Console.WriteLine(privateProtectedField);
}
// 私有方法
private void PrivateMethod()
{
Console.WriteLine("这是私有方法");
}
// 公共方法调用私有方法
public void CallPrivateMethod()
{
PrivateMethod();
}
}
public class ExternalClass
{
public void TestAccess()
{
var demo = new AccessModifierDemo();
// 只能访问public成员
Console.WriteLine(demo.PublicField);
// 以下代码会编译错误
// Console.WriteLine(demo.privateField); // 编译错误
// Console.WriteLine(demo.protectedField); // 编译错误
// internal成员在同一程序集内可以访问
Console.WriteLine(demo.internalField);
Console.WriteLine(demo.protectedInternalField);
}
}
3. 封装
3.1 封装的概念
封装是将数据和操作数据的方法组合在一起,并隐藏内部实现细节的机制。
public class BankAccount
{
// 私有字段,外部无法直接访问
private decimal balance;
private string accountNumber;
private string ownerName;
private List<Transaction> transactions;
// 只读属性
public string AccountNumber => accountNumber;
public string OwnerName => ownerName;
public decimal Balance => balance;
// 只读交易历史
public IReadOnlyList<Transaction> Transactions => transactions.AsReadOnly();
public BankAccount(string accountNumber, string ownerName, decimal initialBalance = 0)
{
if (string.IsNullOrWhiteSpace(accountNumber))
throw new ArgumentException("账号不能为空");
if (string.IsNullOrWhiteSpace(ownerName))
throw new ArgumentException("账户名不能为空");
if (initialBalance < 0)
throw new ArgumentException("初始余额不能为负数");
this.accountNumber = accountNumber;
this.ownerName = ownerName;
this.balance = initialBalance;
this.transactions = new List<Transaction>();
if (initialBalance > 0)
{
transactions.Add(new Transaction("开户", initialBalance, DateTime.Now));
}
}
// 存款方法
public void Deposit(decimal amount, string description = "存款")
{
if (amount <= 0)
throw new ArgumentException("存款金额必须大于0");
balance += amount;
transactions.Add(new Transaction(description, amount, DateTime.Now));
Console.WriteLine($"存款成功:{amount:C},当前余额:{balance:C}");
}
// 取款方法
public bool Withdraw(decimal amount, string description = "取款")
{
if (amount <= 0)
{
Console.WriteLine("取款金额必须大于0");
return false;
}
if (amount > balance)
{
Console.WriteLine($"余额不足,当前余额:{balance:C}");
return false;
}
balance -= amount;
transactions.Add(new Transaction(description, -amount, DateTime.Now));
Console.WriteLine($"取款成功:{amount:C},当前余额:{balance:C}");
return true;
}
// 转账方法
public bool Transfer(BankAccount targetAccount, decimal amount, string description = "转账")
{
if (targetAccount == null)
{
Console.WriteLine("目标账户不能为空");
return false;
}
if (this == targetAccount)
{
Console.WriteLine("不能向自己转账");
return false;
}
if (Withdraw(amount, $"转账给{targetAccount.OwnerName}"))
{
targetAccount.Deposit(amount, $"来自{this.OwnerName}的转账");
Console.WriteLine($"转账成功:向{targetAccount.OwnerName}转账{amount:C}");
return true;
}
return false;
}
// 查询余额
public void CheckBalance()
{
Console.WriteLine($"账户:{accountNumber}");
Console.WriteLine($"户名:{ownerName}");
Console.WriteLine($"余额:{balance:C}");
}
// 打印交易历史
public void PrintTransactionHistory(int count = 10)
{
Console.WriteLine($"\n=== {ownerName}的交易历史 ===");
Console.WriteLine("时间\t\t\t描述\t\t金额");
Console.WriteLine(new string('-', 50));
var recentTransactions = transactions.TakeLast(count);
foreach (var transaction in recentTransactions)
{
string amountStr = transaction.Amount >= 0 ?
$"+{transaction.Amount:C}" :
$"{transaction.Amount:C}";
Console.WriteLine($"{transaction.DateTime:yyyy-MM-dd HH:mm}\t{transaction.Description}\t\t{amountStr}");
}
}
// 计算利息(私有方法)
private decimal CalculateInterest(decimal rate)
{
return balance * rate / 100;
}
// 添加利息(公共接口)
public void AddInterest(decimal annualRate)
{
if (annualRate < 0)
{
Console.WriteLine("利率不能为负数");
return;
}
decimal interest = CalculateInterest(annualRate / 12); // 月利息
if (interest > 0)
{
Deposit(interest, "利息收入");
}
}
}
// 交易记录类
public class Transaction
{
public string Description { get; }
public decimal Amount { get; }
public DateTime DateTime { get; }
public Transaction(string description, decimal amount, DateTime dateTime)
{
Description = description;
Amount = amount;
DateTime = dateTime;
}
}
3.2 属性的高级用法
public class Employee
{
private string firstName;
private string lastName;
private decimal salary;
private DateTime hireDate;
// 基本属性
public string FirstName
{
get => firstName;
set => firstName = value?.Trim() ?? throw new ArgumentNullException(nameof(value));
}
public string LastName
{
get => lastName;
set => lastName = value?.Trim() ?? throw new ArgumentNullException(nameof(value));
}
// 计算属性(只读)
public string FullName => $"{FirstName} {LastName}";
// 带验证的属性
public decimal Salary
{
get => salary;
set
{
if (value < 0)
throw new ArgumentException("薪资不能为负数");
if (value > 1000000)
throw new ArgumentException("薪资不能超过100万");
salary = value;
}
}
// 只读属性
public DateTime HireDate
{
get => hireDate;
private set => hireDate = value; // 只能在类内部设置
}
// 计算工作年限
public int YearsOfService => (DateTime.Now - HireDate).Days / 365;
// 自动属性
public string Department { get; set; }
public string Position { get; set; }
// 带初始值的自动属性
public bool IsActive { get; set; } = true;
public Employee(string firstName, string lastName, decimal salary, string department, string position)
{
FirstName = firstName;
LastName = lastName;
Salary = salary;
Department = department;
Position = position;
HireDate = DateTime.Now;
}
// 属性变化通知(简单版本)
private List<string> propertyChangeLog = new List<string>();
public IReadOnlyList<string> PropertyChangeLog => propertyChangeLog.AsReadOnly();
private void LogPropertyChange(string propertyName, object oldValue, object newValue)
{
string log = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - {propertyName}: {oldValue} -> {newValue}";
propertyChangeLog.Add(log);
}
// 带日志的薪资设置
public void UpdateSalary(decimal newSalary, string reason = "")
{
decimal oldSalary = Salary;
Salary = newSalary;
string logMessage = $"薪资调整: {oldSalary:C} -> {newSalary:C}";
if (!string.IsNullOrWhiteSpace(reason))
{
logMessage += $" (原因: {reason})";
}
propertyChangeLog.Add($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - {logMessage}");
Console.WriteLine(logMessage);
}
public override string ToString()
{
return $"{FullName} - {Position} ({Department}) - {Salary:C} - 工作{YearsOfService}年";
}
}
3.3 索引器
public class StudentGrades
{
private Dictionary<string, decimal> grades;
private string studentName;
public string StudentName => studentName;
public StudentGrades(string studentName)
{
this.studentName = studentName;
this.grades = new Dictionary<string, decimal>();
}
// 字符串索引器
public decimal this[string subject]
{
get
{
if (grades.ContainsKey(subject))
return grades[subject];
return 0; // 默认分数
}
set
{
if (value < 0 || value > 100)
throw new ArgumentException("分数必须在0-100之间");
grades[subject] = value;
}
}
// 整数索引器(按添加顺序)
public KeyValuePair<string, decimal> this[int index]
{
get
{
if (index < 0 || index >= grades.Count)
throw new IndexOutOfRangeException("索引超出范围");
return grades.ElementAt(index);
}
}
// 多参数索引器
public decimal this[string subject, string semester]
{
get
{
string key = $"{subject}_{semester}";
return grades.ContainsKey(key) ? grades[key] : 0;
}
set
{
if (value < 0 || value > 100)
throw new ArgumentException("分数必须在0-100之间");
string key = $"{subject}_{semester}";
grades[key] = value;
}
}
public int SubjectCount => grades.Count;
public decimal AverageGrade
{
get
{
if (grades.Count == 0) return 0;
return grades.Values.Average();
}
}
public void RemoveSubject(string subject)
{
grades.Remove(subject);
}
public bool HasSubject(string subject)
{
return grades.ContainsKey(subject);
}
public void PrintGrades()
{
Console.WriteLine($"\n=== {studentName}的成绩单 ===");
Console.WriteLine("科目\t\t\t分数");
Console.WriteLine(new string('-', 30));
foreach (var grade in grades.OrderBy(g => g.Key))
{
Console.WriteLine($"{grade.Key}\t\t\t{grade.Value:F1}");
}
Console.WriteLine(new string('-', 30));
Console.WriteLine($"平均分:\t\t\t{AverageGrade:F1}");
}
}
// 使用示例
public class IndexerDemo
{
public static void Main()
{
Console.WriteLine("=== 索引器演示 ===");
var student = new StudentGrades("张三");
// 使用字符串索引器
student["数学"] = 95;
student["英语"] = 87;
student["物理"] = 92;
student["化学"] = 89;
// 使用多参数索引器
student["数学", "2023春"] = 93;
student["数学", "2023秋"] = 97;
Console.WriteLine($"数学成绩: {student["数学"]}");
Console.WriteLine($"2023春季数学成绩: {student["数学", "2023春"]}");
// 使用整数索引器
Console.WriteLine("\n按顺序遍历成绩:");
for (int i = 0; i < student.SubjectCount; i++)
{
var grade = student[i];
Console.WriteLine($"{grade.Key}: {grade.Value}");
}
student.PrintGrades();
}
}
4. 静态成员
4.1 静态字段和属性
public class Counter
{
// 静态字段 - 所有实例共享
private static int totalCount = 0;
private static readonly object lockObject = new object();
// 实例字段
private int instanceId;
private string name;
// 静态属性
public static int TotalCount
{
get
{
lock (lockObject)
{
return totalCount;
}
}
}
// 实例属性
public int InstanceId => instanceId;
public string Name => name;
// 静态构造函数 - 只执行一次
static Counter()
{
Console.WriteLine("静态构造函数执行:初始化静态成员");
totalCount = 0;
}
// 实例构造函数
public Counter(string name)
{
lock (lockObject)
{
totalCount++;
this.instanceId = totalCount;
}
this.name = name;
Console.WriteLine($"创建Counter实例: {name} (ID: {instanceId})");
}
// 静态方法
public static void ResetCount()
{
lock (lockObject)
{
totalCount = 0;
}
Console.WriteLine("计数器已重置");
}
public static Counter CreateCounter(string name)
{
return new Counter(name);
}
// 实例方法
public void DisplayInfo()
{
Console.WriteLine($"Counter: {name}, ID: {instanceId}, 总数: {TotalCount}");
}
}
4.2 静态类
// 静态类 - 不能被实例化,只能包含静态成员
public static class MathHelper
{
// 静态常量
public const double PI = 3.14159265359;
public const double E = 2.71828182846;
// 静态只读字段
public static readonly Random Random = new Random();
// 静态方法
public static double CalculateCircleArea(double radius)
{
if (radius < 0)
throw new ArgumentException("半径不能为负数");
return PI * radius * radius;
}
public static double CalculateCircleCircumference(double radius)
{
if (radius < 0)
throw new ArgumentException("半径不能为负数");
return 2 * PI * radius;
}
public static int GetRandomNumber(int min, int max)
{
return Random.Next(min, max + 1);
}
public static double Power(double baseNumber, int exponent)
{
if (exponent == 0) return 1;
if (exponent == 1) return baseNumber;
double result = 1;
bool isNegativeExponent = exponent < 0;
exponent = Math.Abs(exponent);
for (int i = 0; i < exponent; i++)
{
result *= baseNumber;
}
return isNegativeExponent ? 1 / result : result;
}
public static bool IsPrime(int number)
{
if (number < 2) return false;
if (number == 2) return true;
if (number % 2 == 0) return false;
for (int i = 3; i * i <= number; i += 2)
{
if (number % i == 0)
return false;
}
return true;
}
public static int Factorial(int n)
{
if (n < 0)
throw new ArgumentException("阶乘的参数不能为负数");
if (n <= 1) return 1;
int result = 1;
for (int i = 2; i <= n; i++)
{
result *= i;
}
return result;
}
public static int GreatestCommonDivisor(int a, int b)
{
a = Math.Abs(a);
b = Math.Abs(b);
while (b != 0)
{
int temp = b;
b = a % b;
a = temp;
}
return a;
}
public static int LeastCommonMultiple(int a, int b)
{
return Math.Abs(a * b) / GreatestCommonDivisor(a, b);
}
}
// 扩展方法(必须在静态类中定义)
public static class StringExtensions
{
public static bool IsValidEmail(this string email)
{
if (string.IsNullOrWhiteSpace(email))
return false;
try
{
var addr = new System.Net.Mail.MailAddress(email);
return addr.Address == email;
}
catch
{
return false;
}
}
public static string Reverse(this string str)
{
if (string.IsNullOrEmpty(str))
return str;
char[] chars = str.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
public static int WordCount(this string str)
{
if (string.IsNullOrWhiteSpace(str))
return 0;
return str.Split(new char[] { ' ', '\t', '\n', '\r' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
public static string ToPascalCase(this string str)
{
if (string.IsNullOrWhiteSpace(str))
return str;
var words = str.Split(new char[] { ' ', '_', '-' },
StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < words.Length; i++)
{
if (words[i].Length > 0)
{
words[i] = char.ToUpper(words[i][0]) +
(words[i].Length > 1 ? words[i].Substring(1).ToLower() : "");
}
}
return string.Join("", words);
}
}
4.3 静态成员使用示例
public class StaticMemberDemo
{
public static void Main()
{
Console.WriteLine("=== 静态成员演示 ===");
// 使用静态类的方法
Console.WriteLine($"圆周率: {MathHelper.PI}");
Console.WriteLine($"半径为5的圆面积: {MathHelper.CalculateCircleArea(5):F2}");
Console.WriteLine($"半径为5的圆周长: {MathHelper.CalculateCircleCircumference(5):F2}");
Console.WriteLine($"\n随机数: {MathHelper.GetRandomNumber(1, 100)}");
Console.WriteLine($"2的10次方: {MathHelper.Power(2, 10)}");
Console.WriteLine($"7是质数吗: {MathHelper.IsPrime(7)}");
Console.WriteLine($"5的阶乘: {MathHelper.Factorial(5)}");
Console.WriteLine($"12和18的最大公约数: {MathHelper.GreatestCommonDivisor(12, 18)}");
Console.WriteLine($"12和18的最小公倍数: {MathHelper.LeastCommonMultiple(12, 18)}");
// 使用扩展方法
string email = "test@example.com";
Console.WriteLine($"\n{email} 是有效邮箱吗: {email.IsValidEmail()}");
string text = "Hello World";
Console.WriteLine($"'{text}' 反转后: '{text.Reverse()}'";
Console.WriteLine($"'{text}' 单词数: {text.WordCount()}");
Console.WriteLine($"'hello_world' 转换为PascalCase: '{"hello_world".ToPascalCase()}'");
// 使用Counter类演示静态成员
Console.WriteLine($"\n=== Counter演示 ===");
Console.WriteLine($"初始计数: {Counter.TotalCount}");
var counter1 = new Counter("计数器1");
var counter2 = new Counter("计数器2");
var counter3 = new Counter("计数器3");
counter1.DisplayInfo();
counter2.DisplayInfo();
counter3.DisplayInfo();
Console.WriteLine($"\n总计数: {Counter.TotalCount}");
Counter.ResetCount();
Console.WriteLine($"重置后计数: {Counter.TotalCount}");
var counter4 = Counter.CreateCounter("计数器4");
counter4.DisplayInfo();
}
}
5. 总结
本章介绍了面向对象编程的基础概念:
- 面向对象概述:理解OOP的核心思想和优势
- 类和对象:类的定义、对象的创建和使用
- 访问修饰符:控制成员的可见性
- 封装:通过属性和方法隐藏内部实现
- 属性和索引器:提供对数据的受控访问
- 静态成员:类级别的数据和方法
关键要点: - 合理使用访问修饰符保护数据 - 通过属性提供数据验证和封装 - 静态成员属于类而不是实例 - 扩展方法可以为现有类型添加功能 - 索引器提供类似数组的访问方式
下一章预告: 下一章我们将深入学习继承和多态,这是面向对象编程的核心特性。