本章目标

通过本章学习,你将掌握: - 面向对象编程的核心概念 - 类和对象的定义与使用 - 封装、继承、多态的实现 - 构造函数和析构函数 - 访问修饰符的使用 - 静态成员和实例成员 - 属性和索引器 - 方法重载和重写

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. 总结

本章介绍了面向对象编程的基础概念:

  1. 面向对象概述:理解OOP的核心思想和优势
  2. 类和对象:类的定义、对象的创建和使用
  3. 访问修饰符:控制成员的可见性
  4. 封装:通过属性和方法隐藏内部实现
  5. 属性和索引器:提供对数据的受控访问
  6. 静态成员:类级别的数据和方法

关键要点: - 合理使用访问修饰符保护数据 - 通过属性提供数据验证和封装 - 静态成员属于类而不是实例 - 扩展方法可以为现有类型添加功能 - 索引器提供类似数组的访问方式

下一章预告: 下一章我们将深入学习继承和多态,这是面向对象编程的核心特性。