C# 零基础到精通教程 - 第七章:面向对象编程(入门)——类与对象

7.1 什么是面向对象?

7.1.1 面向过程 vs 面向对象

面向过程(Procedural Programming)

关注的是"步骤"------做一件事需要哪些步骤,按什么顺序执行。

csharp

复制代码
// 面向过程:模拟一个人吃饭
string name = "张三";
int age = 25;
double height = 1.75;

void Eat(string personName)
{
    Console.WriteLine($"{personName}正在吃饭");
}

Eat(name);

面向对象(Object-Oriented Programming)

关注的是"对象"------把数据和操作数据的方法打包在一起。

csharp

复制代码
// 面向对象:定义"人"这个类
class Person
{
    public string Name;     // 属性(数据)
    public int Age;
    public double Height;
    
    public void Eat()       // 方法(行为)
    {
        Console.WriteLine($"{Name}正在吃饭");
    }
}

// 使用
Person person = new Person();
person.Name = "张三";
person.Eat();

7.1.2 现实世界中的对象

text

复制代码
┌─────────────────────────────────────────────────────────┐
│                      现实世界                             │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   手机                   汽车                   学生      │
│   - 品牌                 - 品牌                 - 姓名    │
│   - 型号                 - 颜色                 - 学号    │
│   - 价格                 - 排量                 - 成绩    │
│   - 开机()               - 启动()               - 学习()  │
│   - 关机()               - 加速()               - 考试()  │
│   - 拍照()               - 刹车()               - 休息()  │
│                                                         │
└─────────────────────────────────────────────────────────┘
         ↓ 编程世界 ↓
┌─────────────────────────────────────────────────────────┐
│                        C# 类                             │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   class Phone              class Car           class Student
│   {                        {                   {
│       string Brand;            string Brand;        string Name;
│       string Model;            string Color;        int StudentId;
│       double Price;            double EngineSize;   double Score;
│                              │                     │
│       void PowerOn() {}       void Start() {}      void Study() {}
│       void PowerOff() {}      void Accelerate() {} void Exam() {}
│   }                        }                   }    │
│                                                         │
└─────────────────────────────────────────────────────────┘

7.1.3 面向对象的三大特性

特性 含义 类比
封装 把数据和操作数据的方法打包在一起,隐藏内部细节 手机:你按按钮就能打电话,不需要知道电路怎么工作
继承 子类可以复用父类的代码,并扩展新功能 孩子继承父母的基因,还有自己的特点
多态 同一个方法在不同对象上有不同的表现 动物都会"叫",但狗"汪汪",猫"喵喵"

本章重点学习封装(类和对象),继承和多态将在后续章节详细讲解。


7.2 类与对象的基本概念

7.2.1 类 vs 对象

类 = 蓝图/模板(概念上的定义)
对象 = 根据蓝图创建出来的具体实例(真实存在的实体)

csharp

复制代码
// 类:定义"人"是什么样子的(蓝图)
class Person
{
    public string Name;
    public int Age;
    
    public void SayHello()
    {
        Console.WriteLine($"你好,我是{Name},今年{Age}岁");
    }
}

// 对象:根据 Person 这个类创建的具体的人
Person person1 = new Person();  // 创建对象
person1.Name = "张三";           // 赋值
person1.Age = 25;
person1.SayHello();              // 调用方法

Person person2 = new Person();  // 另一个对象
person2.Name = "李四";
person2.Age = 30;
person2.SayHello();

类比理解

text

复制代码
┌─────────────────┐      ┌─────────────────┐      ┌─────────────────┐
│     Person      │      │    person1      │      │    person2      │
│      (类)       │ ──→  │     (对象)      │ ──→  │     (对象)      │
├─────────────────┤      ├─────────────────┤      ├─────────────────┤
│     蓝图        │      │ 张三,25岁      │      │ 李四,30岁      │
│     模板        │      │ 具体的人        │      │ 具体的人        │
└─────────────────┘      └─────────────────┘      └─────────────────┘

     菜谱                  第一盘菜                第二盘菜

7.2.2 类的定义

csharp

复制代码
// 基本语法
[访问修饰符] class 类名
{
    // 字段(存储数据)
    // 属性(访问字段的入口,后面细讲)
    // 方法(行为)
    // 构造函数(创建对象时初始化)
}

最简单的类

csharp

复制代码
class Student
{
    // 字段(通常 private,但为了简化,先用 public)
    public string Name;
    public int Age;
    public string StudentId;
    
    // 方法
    public void Study()
    {
        Console.WriteLine($"{Name}正在学习");
    }
    
    public void ShowInfo()
    {
        Console.WriteLine($"姓名:{Name},学号:{StudentId},年龄:{Age}");
    }
}

7.2.3 创建对象(实例化)

csharp

复制代码
// 语法:类名 对象名 = new 类名();
Student stu1 = new Student();
Student stu2 = new Student();

// 或者分两步
Student stu3;
stu3 = new Student();

7.2.4 访问成员

csharp

复制代码
// 使用点运算符 . 访问对象的字段和方法
Student stu = new Student();
stu.Name = "张三";           // 设置字段
stu.Age = 20;
stu.StudentId = "20240001";

string name = stu.Name;      // 读取字段
stu.ShowInfo();              // 调用方法
stu.Study();                 // 调用方法

7.3 字段(Field)

7.3.1 字段的基本使用

字段 = 类中存储数据的变量

csharp

复制代码
class Car
{
    // 字段(也叫成员变量)
    public string brand;      // 品牌
    public string model;      // 型号
    public int year;          // 年份
    public double price;      // 价格
    public string color;      // 颜色
}

// 使用
Car myCar = new Car();
myCar.brand = "特斯拉";
myCar.model = "Model 3";
myCar.year = 2024;
myCar.price = 299900;
myCar.color = "黑色";

Console.WriteLine($"我买了一辆{myCar.year}年款的{myCar.color}{myCar.brand}{myCar.model},价格{myCar.price}元");

7.3.2 字段的默认值

csharp

复制代码
class TestClass
{
    public int intField;        // 默认 0
    public double doubleField;  // 默认 0.0
    public bool boolField;      // 默认 false
    public string stringField;  // 默认 null
    public char charField;      // 默认 '\0'
}

TestClass obj = new TestClass();
Console.WriteLine($"int: {obj.intField}");       // 0
Console.WriteLine($"double: {obj.doubleField}"); // 0
Console.WriteLine($"bool: {obj.boolField}");     // False
Console.WriteLine($"string: {obj.stringField}"); // (空)

7.3.3 字段初始化

csharp

复制代码
class Product
{
    // 声明时直接赋初始值
    public string name = "未命名";
    public double price = 0.0;
    public int stock = 0;
    public string category = "未分类";
}

Product p = new Product();
Console.WriteLine($"产品:{p.name},价格:{p.price},库存:{p.stock}");

7.4 方法(Method)

7.4.1 实例方法

前面学的 static 方法是"类方法",不写 static 的是"实例方法",需要通过对象调用

csharp

复制代码
class Calculator
{
    // 实例方法(非静态)
    public int Add(int a, int b)
    {
        return a + b;
    }
    
    public int Multiply(int a, int b)
    {
        return a * b;
    }
}

// 使用
Calculator calc = new Calculator();  // 先创建对象
int sum = calc.Add(5, 3);            // 通过对象调用
int product = calc.Multiply(5, 3);

静态方法 vs 实例方法

csharp

复制代码
class MathHelper
{
    // 静态方法:可以直接通过类名调用
    public static int AddStatic(int a, int b)
    {
        return a + b;
    }
    
    // 实例方法:需要通过对象调用
    public int AddInstance(int a, int b)
    {
        return a + b;
    }
}

// 调用
int result1 = MathHelper.AddStatic(5, 3);     // ✅ 类名.方法名
MathHelper helper = new MathHelper();
int result2 = helper.AddInstance(5, 3);       // ✅ 对象名.方法名

7.4.2 方法访问字段

实例方法可以直接访问同一个对象的字段

csharp

复制代码
class BankAccount
{
    public string accountNumber;
    public string owner;
    public double balance;
    
    // 存款
    public void Deposit(double amount)
    {
        if (amount > 0)
        {
            balance += amount;
            Console.WriteLine($"存入{amount}元,当前余额:{balance}元");
        }
        else
        {
            Console.WriteLine("存款金额必须大于0");
        }
    }
    
    // 取款
    public void Withdraw(double amount)
    {
        if (amount > 0 && amount <= balance)
        {
            balance -= amount;
            Console.WriteLine($"取出{amount}元,当前余额:{balance}元");
        }
        else if (amount > balance)
        {
            Console.WriteLine("余额不足!");
        }
        else
        {
            Console.WriteLine("取款金额必须大于0");
        }
    }
    
    // 查询余额
    public void ShowBalance()
    {
        Console.WriteLine($"账户:{accountNumber},户主:{owner},余额:{balance}元");
    }
}

// 使用
BankAccount account = new BankAccount();
account.accountNumber = "6228480012345678";
account.owner = "张三";
account.balance = 1000;

account.ShowBalance();
account.Deposit(500);
account.Withdraw(200);
account.ShowBalance();

7.4.3 this 关键字

this 代表当前对象本身,用于区分字段和局部变量

csharp

复制代码
class Person
{
    public string name;
    public int age;
    
    // 当参数名和字段名相同时,使用 this 区分
    public void SetInfo(string name, int age)
    {
        this.name = name;  // this.name 是字段,name 是参数
        this.age = age;
    }
    
    public void ShowInfo()
    {
        Console.WriteLine($"姓名:{this.name},年龄:{this.age}");
        // this 可以省略,但有时保留更清晰
    }
}

7.5 构造函数(Constructor)

7.5.1 什么是构造函数?

构造函数 = 创建对象时自动调用的特殊方法,用于初始化对象

csharp

复制代码
class Student
{
    public string Name;
    public int Age;
    public string Major;
    
    // 构造函数:名字与类名相同,没有返回值类型(连 void 都不写)
    public Student()
    {
        Console.WriteLine("构造函数被调用了");
        Name = "未知";
        Age = 18;
        Major = "未选择";
    }
}

// 创建对象时会自动调用构造函数
Student stu = new Student();  // 输出:构造函数被调用了
Console.WriteLine($"姓名:{stu.Name}");  // 未知

7.5.2 带参数的构造函数

csharp

复制代码
class Student
{
    public string Name;
    public int Age;
    public string Major;
    
    // 无参构造函数
    public Student()
    {
        Name = "未知";
        Age = 18;
        Major = "未选择";
    }
    
    // 带参数的构造函数
    public Student(string name, int age, string major)
    {
        Name = name;
        Age = age;
        Major = major;
    }
    
    public void ShowInfo()
    {
        Console.WriteLine($"姓名:{Name},年龄:{Age},专业:{Major}");
    }
}

// 使用不同的构造函数
Student stu1 = new Student();                    // 使用无参构造函数
Student stu2 = new Student("张三", 20, "计算机"); // 使用带参构造函数

stu1.ShowInfo();  // 姓名:未知,年龄:18,专业:未选择
stu2.ShowInfo();  // 姓名:张三,年龄:20,专业:计算机

7.5.3 多个构造函数(重载)

csharp

复制代码
class Product
{
    public string Name;
    public double Price;
    public int Stock;
    
    // 构造函数1:全部参数
    public Product(string name, double price, int stock)
    {
        Name = name;
        Price = price;
        Stock = stock;
    }
    
    // 构造函数2:只有名称和价格,库存默认0
    public Product(string name, double price) : this(name, price, 0)
    {
        // : this(...) 表示调用另一个构造函数
    }
    
    // 构造函数3:只有名称,价格默认0,库存默认0
    public Product(string name) : this(name, 0, 0)
    {
    }
    
    public void ShowInfo()
    {
        Console.WriteLine($"产品:{Name},价格:{Price},库存:{Stock}");
    }
}

// 使用
Product p1 = new Product("iPhone", 6999, 100);
Product p2 = new Product("iPad", 3999);
Product p3 = new Product("充电器");

p1.ShowInfo();  // iPhone,6999,100
p2.ShowInfo();  // iPad,3999,0
p3.ShowInfo();  // 充电器,0,0

7.5.4 默认构造函数

如果类中没有定义任何构造函数,编译器会自动生成一个无参构造函数

csharp

复制代码
class SimpleClass
{
    public int Value;
    // 没有定义构造函数,编译器会生成一个默认的无参构造函数
}

SimpleClass obj = new SimpleClass();  // 可以创建
Console.WriteLine(obj.Value);         // 0(默认值)

如果定义了带参数的构造函数,编译器不会再生成无参构造函数

csharp

复制代码
class Person
{
    public string Name;
    
    public Person(string name)  // 只定义了带参构造函数
    {
        Name = name;
    }
}

// Person p = new Person();  // ❌ 错误!没有无参构造函数
Person p = new Person("张三");  // ✅ 必须使用带参构造函数

7.6 属性(Property)

7.6.1 为什么需要属性?

问题:字段直接暴露,缺乏控制

csharp

复制代码
class BankAccount
{
    public double balance;  // 字段是 public,任何人都可以随意修改
}

BankAccount acc = new BankAccount();
acc.balance = -1000;  // 余额可以是负数,这不合理!

解决方案:使用属性控制访问

csharp

复制代码
class BankAccount
{
    private double balance;  // 字段设为 private(私有)
    
    // 属性:提供受控的访问
    public double Balance
    {
        get { return balance; }      // 读取时返回
        set                          // 写入时验证
        {
            if (value >= 0)
                balance = value;
            else
                Console.WriteLine("余额不能为负数!");
        }
    }
}

BankAccount acc = new BankAccount();
acc.Balance = 100;      // 调用 set
Console.WriteLine(acc.Balance);  // 调用 get,输出 100
acc.Balance = -500;     // 输出:余额不能为负数!(不会修改)

7.6.2 属性的基本语法

csharp

复制代码
class Person
{
    private string name;
    private int age;
    
    // 完整属性写法
    public string Name
    {
        get { return name; }      // get 访问器
        set { name = value; }     // set 访问器,value 是传入的值
    }
    
    // 可以在 get/set 中添加逻辑
    public int Age
    {
        get { return age; }
        set 
        {
            if (value >= 0 && value <= 150)
                age = value;
            else
                Console.WriteLine("年龄必须在0-150之间");
        }
    }
}

7.6.3 自动实现属性

如果属性没有额外逻辑,可以使用更简洁的自动属性

csharp

复制代码
class Student
{
    // 自动属性:编译器自动生成隐藏的字段
    public string Name { get; set; }
    public int Age { get; set; }
    public string Major { get; set; }
}

// 使用
Student stu = new Student();
stu.Name = "张三";
stu.Age = 20;
Console.WriteLine($"{stu.Name},{stu.Age}岁");

7.6.4 只读属性和只写属性

csharp

复制代码
class ReadOnlyExample
{
    private string id = "ID001";
    
    // 只读属性:只有 get,没有 set
    public string Id
    {
        get { return id; }
    }
    
    // 自动实现只读属性(只能在构造函数中赋值)
    public string Name { get; }
    
    // 自动实现只写属性(少见)
    private string password;
    public string Password
    {
        set { password = value; }
    }
    
    public ReadOnlyExample(string name)
    {
        Name = name;  // 只能在构造函数中给只读属性赋值
    }
}

ReadOnlyExample obj = new ReadOnlyExample("张三");
Console.WriteLine(obj.Id);     // 可以读
Console.WriteLine(obj.Name);   // 可以读
// obj.Id = "002";            // ❌ 不能写
// obj.Name = "李四";         // ❌ 不能写

7.6.5 表达式体属性(C# 7.0+)

csharp

复制代码
class Circle
{
    public double Radius { get; set; }
    
    // 只读属性的简化写法
    public double Area => Math.PI * Radius * Radius;
    public double Circumference => 2 * Math.PI * Radius;
    
    // 等同于
    // public double Area 
    // {
    //     get { return Math.PI * Radius * Radius; }
    // }
}

Circle c = new Circle { Radius = 5 };
Console.WriteLine($"面积:{c.Area:F2}");
Console.WriteLine($"周长:{c.Circumference:F2}");

7.7 访问修饰符

访问修饰符控制类、字段、方法等成员的可见范围

7.7.1 常用访问修饰符

修饰符 访问权限 类比
public 任何地方都能访问 公共场所
private 只在当前类内能访问 私人卧室
protected 当前类和派生类能访问 家庭成员
internal 同一程序集内能访问 公司内部
protected internal 同一程序集或派生类 公司内部+家庭成员
private protected 同一程序集内的派生类 公司里的家庭成员

7.7.2 实际应用

csharp

复制代码
public class BankAccount  // public:这个类可以被其他项目使用
{
    private string password;  // private:密码只在类内部使用
    private double balance;   // private:余额不能直接访问
    
    public string AccountNumber { get; private set; }  // 外部可读,内部可写
    public string Owner { get; set; }  // 外部可读写
    
    protected double InterestRate { get; set; }  // 子类可以访问
    
    public void Deposit(double amount)  // public:存款方法所有人都能用
    {
        if (amount > 0)
            balance += amount;
    }
    
    private bool CheckPassword(string pwd)  // private:密码验证内部使用
    {
        return password == pwd;
    }
}

7.7.3 默认访问级别

csharp

复制代码
// 类默认是 internal(同一程序集内可见)
class DefaultClass  // 等同于 internal class DefaultClass
{
    // 字段/方法默认是 private
    int number;           // 等同于 private int number;
    void Method() { }     // 等同于 private void Method() { }
    
    public int PublicField;   // 明确指定 public
}

7.8 综合示例

示例1:简单的学生管理系统(类与对象版)

csharp

复制代码
using System;
using System.Collections.Generic;

class Student
{
    // 自动属性
    public string StudentId { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Major { get; set; }
    public double Score { get; set; }
    
    // 构造函数
    public Student(string studentId, string name, int age, string major, double score)
    {
        StudentId = studentId;
        Name = name;
        Age = age;
        Major = major;
        Score = score;
    }
    
    // 方法:显示学生信息
    public void ShowInfo()
    {
        string grade = GetGrade();
        Console.WriteLine($"{StudentId}\t{Name}\t{Age}\t{Major}\t{Score}\t{grade}");
    }
    
    // 方法:获取等级
    private string GetGrade()
    {
        if (Score >= 90) return "A";
        if (Score >= 80) return "B";
        if (Score >= 70) return "C";
        if (Score >= 60) return "D";
        return "F";
    }
    
    // 方法:判断是否及格
    public bool IsPassed()
    {
        return Score >= 60;
    }
}

class StudentManager
{
    private List<Student> students = new List<Student>();
    
    public void AddStudent(Student student)
    {
        students.Add(student);
        Console.WriteLine($"成功添加学生:{student.Name}");
    }
    
    public void ShowAllStudents()
    {
        if (students.Count == 0)
        {
            Console.WriteLine("暂无学生数据");
            return;
        }
        
        Console.WriteLine("\n学号\t姓名\t年龄\t专业\t成绩\t等级");
        Console.WriteLine("--------------------------------------------");
        foreach (Student s in students)
        {
            s.ShowInfo();
        }
    }
    
    public void ShowStatistics()
    {
        if (students.Count == 0) return;
        
        double sum = 0;
        double max = double.MinValue;
        double min = double.MaxValue;
        int passCount = 0;
        
        foreach (Student s in students)
        {
            sum += s.Score;
            if (s.Score > max) max = s.Score;
            if (s.Score < min) min = s.Score;
            if (s.IsPassed()) passCount++;
        }
        
        Console.WriteLine("\n=== 统计信息 ===");
        Console.WriteLine($"学生总数:{students.Count}");
        Console.WriteLine($"平均分:{(sum / students.Count):F2}");
        Console.WriteLine($"最高分:{max}");
        Console.WriteLine($"最低分:{min}");
        Console.WriteLine($"及格人数:{passCount}({(double)passCount / students.Count * 100:F1}%)");
    }
    
    public Student FindStudent(string studentId)
    {
        foreach (Student s in students)
        {
            if (s.StudentId == studentId)
                return s;
        }
        return null;
    }
}

class Program
{
    static void Main()
    {
        StudentManager manager = new StudentManager();
        
        // 添加学生
        manager.AddStudent(new Student("001", "张三", 20, "计算机", 85.5));
        manager.AddStudent(new Student("002", "李四", 21, "软件工程", 92));
        manager.AddStudent(new Student("003", "王五", 19, "计算机", 67.5));
        manager.AddStudent(new Student("004", "赵六", 22, "人工智能", 78));
        
        // 显示所有学生
        manager.ShowAllStudents();
        
        // 统计信息
        manager.ShowStatistics();
        
        // 查找学生
        Console.Write("\n请输入要查找的学号:");
        string id = Console.ReadLine();
        Student found = manager.FindStudent(id);
        if (found != null)
        {
            Console.WriteLine("找到学生:");
            found.ShowInfo();
        }
        else
        {
            Console.WriteLine("未找到该学生");
        }
    }
}

示例2:银行账户系统

csharp

复制代码
using System;

class BankAccount
{
    // 字段
    private string accountNumber;
    private string owner;
    private double balance;
    private string password;
    private static int accountCounter = 1000;  // 静态字段,所有账户共享
    
    // 属性
    public string AccountNumber 
    { 
        get { return accountNumber; }
        private set { accountNumber = value; }
    }
    
    public string Owner 
    { 
        get { return owner; }
        private set { owner = value; }
    }
    
    public double Balance 
    { 
        get { return balance; }
        private set { balance = value; }
    }
    
    // 只读属性
    public bool IsActive { get; private set; }
    
    // 构造函数
    public BankAccount(string owner, string password, double initialDeposit)
    {
        this.owner = owner;
        this.password = password;
        this.balance = initialDeposit >= 0 ? initialDeposit : 0;
        this.IsActive = true;
        this.accountNumber = GenerateAccountNumber();
        
        Console.WriteLine($"账户创建成功!账号:{accountNumber},初始余额:{balance}元");
    }
    
    // 静态方法:生成账号
    private static string GenerateAccountNumber()
    {
        return "6228" + (++accountCounter).ToString().PadLeft(8, '0');
    }
    
    // 验证密码
    private bool VerifyPassword(string pwd)
    {
        return password == pwd;
    }
    
    // 存款
    public void Deposit(double amount)
    {
        if (!IsActive)
        {
            Console.WriteLine("账户已冻结,无法存款");
            return;
        }
        
        if (amount <= 0)
        {
            Console.WriteLine("存款金额必须大于0");
            return;
        }
        
        balance += amount;
        Console.WriteLine($"成功存入{amount}元,当前余额:{balance}元");
    }
    
    // 取款
    public bool Withdraw(double amount, string pwd)
    {
        if (!IsActive)
        {
            Console.WriteLine("账户已冻结,无法取款");
            return false;
        }
        
        if (!VerifyPassword(pwd))
        {
            Console.WriteLine("密码错误");
            return false;
        }
        
        if (amount <= 0)
        {
            Console.WriteLine("取款金额必须大于0");
            return false;
        }
        
        if (amount > balance)
        {
            Console.WriteLine("余额不足");
            return false;
        }
        
        balance -= amount;
        Console.WriteLine($"成功取出{amount}元,当前余额:{balance}元");
        return true;
    }
    
    // 转账
    public bool Transfer(BankAccount target, double amount, string pwd)
    {
        if (!IsActive || !target.IsActive)
        {
            Console.WriteLine("账户已冻结,无法转账");
            return false;
        }
        
        if (!VerifyPassword(pwd))
        {
            Console.WriteLine("密码错误");
            return false;
        }
        
        if (amount <= 0)
        {
            Console.WriteLine("转账金额必须大于0");
            return false;
        }
        
        if (amount > balance)
        {
            Console.WriteLine("余额不足");
            return false;
        }
        
        balance -= amount;
        target.balance += amount;
        Console.WriteLine($"成功转账{amount}元到账户{target.accountNumber}");
        return true;
    }
    
    // 查询余额
    public void ShowBalance(string pwd)
    {
        if (!VerifyPassword(pwd))
        {
            Console.WriteLine("密码错误");
            return;
        }
        
        Console.WriteLine($"账户:{accountNumber},户主:{owner},余额:{balance}元");
    }
    
    // 显示账户信息
    public void ShowInfo()
    {
        Console.WriteLine($"账号:{accountNumber},户主:{owner},状态:{(IsActive ? "正常" : "冻结")}");
    }
    
    // 冻结账户
    public void Freeze()
    {
        IsActive = false;
        Console.WriteLine("账户已冻结");
    }
    
    // 解冻账户
    public void Unfreeze()
    {
        IsActive = true;
        Console.WriteLine("账户已解冻");
    }
}

class Program
{
    static void Main()
    {
        // 创建账户
        BankAccount acc1 = new BankAccount("张三", "123456", 1000);
        BankAccount acc2 = new BankAccount("李四", "654321", 500);
        
        Console.WriteLine();
        
        // 存款
        acc1.Deposit(500);
        
        // 取款
        acc1.Withdraw(200, "123456");
        acc1.Withdraw(2000, "123456");  // 余额不足
        
        // 查询余额
        acc1.ShowBalance("123456");
        
        Console.WriteLine();
        
        // 转账
        acc1.Transfer(acc2, 300, "123456");
        
        Console.WriteLine();
        acc1.ShowBalance("123456");
        acc2.ShowBalance("654321");
    }
}

示例3:简单的时钟类

csharp

复制代码
using System;
using System.Threading;

class Clock
{
    // 字段
    private int hour;
    private int minute;
    private int second;
    
    // 属性(带验证)
    public int Hour
    {
        get { return hour; }
        set
        {
            if (value >= 0 && value <= 23)
                hour = value;
            else
                Console.WriteLine("小时必须在0-23之间");
        }
    }
    
    public int Minute
    {
        get { return minute; }
        set
        {
            if (value >= 0 && value <= 59)
                minute = value;
            else
                Console.WriteLine("分钟必须在0-59之间");
        }
    }
    
    public int Second
    {
        get { return second; }
        set
        {
            if (value >= 0 && value <= 59)
                second = value;
            else
                Console.WriteLine("秒必须在0-59之间");
        }
    }
    
    // 只读属性:返回当前时间的字符串
    public string TimeString => $"{hour:D2}:{minute:D2}:{second:D2}";
    
    // 构造函数
    public Clock(int hour = 0, int minute = 0, int second = 0)
    {
        Hour = hour;
        Minute = minute;
        Second = second;
    }
    
    // 设置时间
    public void SetTime(int hour, int minute, int second)
    {
        Hour = hour;
        Minute = minute;
        Second = second;
    }
    
    // 增加一秒(处理进位)
    public void Tick()
    {
        second++;
        if (second >= 60)
        {
            second = 0;
            minute++;
            if (minute >= 60)
            {
                minute = 0;
                hour++;
                if (hour >= 24)
                {
                    hour = 0;
                }
            }
        }
    }
    
    // 运行时钟(持续走动)
    public void Run(int seconds)
    {
        Console.WriteLine($"开始运行时钟,共{seconds}秒");
        for (int i = 0; i < seconds; i++)
        {
            Console.Write($"\r{TimeString}");  // \r 使光标回到行首
            Tick();
            Thread.Sleep(1000);  // 等待1秒
        }
        Console.WriteLine("\n时钟停止");
    }
    
    // 显示时间
    public void ShowTime()
    {
        Console.WriteLine($"当前时间:{TimeString}");
    }
    
    // 比较两个时钟的时间是否相同
    public bool IsSameTime(Clock other)
    {
        return this.hour == other.hour && 
               this.minute == other.minute && 
               this.second == other.second;
    }
    
    // 计算两个时钟的时间差(秒)
    public int TimeDifference(Clock other)
    {
        int thisTotalSeconds = hour * 3600 + minute * 60 + second;
        int otherTotalSeconds = other.hour * 3600 + other.minute * 60 + other.second;
        return Math.Abs(thisTotalSeconds - otherTotalSeconds);
    }
}

class Program
{
    static void Main()
    {
        // 创建时钟
        Clock clock1 = new Clock(10, 30, 0);
        Clock clock2 = new Clock();
        clock2.SetTime(14, 45, 30);
        
        // 显示时间
        clock1.ShowTime();
        clock2.ShowTime();
        
        // 时间差
        Console.WriteLine($"时间差:{clock1.TimeDifference(clock2)}秒");
        
        // 运行时钟
        Console.WriteLine("\n按任意键开始运行时钟5秒...");
        Console.ReadKey();
        
        Clock runningClock = new Clock(23, 59, 55);
        runningClock.Run(10);
    }
}

7.9 常见错误与陷阱

错误1:忘记创建对象

csharp

复制代码
Student stu;           // 只是声明了引用,没有创建对象
stu.Name = "张三";     // ❌ NullReferenceException

// 正确
Student stu = new Student();
stu.Name = "张三";

错误2:静态方法中访问实例成员

csharp

复制代码
class MyClass
{
    public int instanceField = 10;
    
    public static void StaticMethod()
    {
        Console.WriteLine(instanceField);  // ❌ 错误!不能直接访问实例成员
    }
}

// 正确方式
public static void StaticMethod()
{
    MyClass obj = new MyClass();
    Console.WriteLine(obj.instanceField);  // ✅ 通过对象访问
}

错误3:私有成员外部访问

csharp

复制代码
class Person
{
    private string secret;
}

Person p = new Person();
p.secret = "xxx";  // ❌ 错误!private 不可访问

错误4:属性中忘记 value

csharp

复制代码
public int Age
{
    get { return age; }
    set { age = value; }  // value 是关键字,不能改名
}

错误5:构造函数没有赋值所有字段

csharp

复制代码
class Product
{
    public string Name;
    public double Price;
    
    public Product(string name)
    {
        Name = name;
        // Price 没有赋值,默认 0.0
    }
}

7.10 本章总结

核心知识点导图

text

复制代码
面向对象编程
├── 类(Class)—— 蓝图/模板
│   ├── 字段 —— 存储数据
│   ├── 方法 —— 定义行为
│
相关推荐
rockey6271 小时前
AScript异步执行与await关键字
c#·.net·script·eval·expression·异步执行·动态脚本
AI科技星1 小时前
《数学公理体系·第三部·数术几何》(2026 年版)
c语言·开发语言·线性代数·算法·矩阵·量子计算·agi
审判长烧鸡2 小时前
【Go工具】go-playground是什么组织?官方的?
开发语言·安全·go
kkeeper~2 小时前
0基础C语言积跬步之字符函数与字符串函数(上)
c语言·开发语言
hhb_6183 小时前
Swift核心技术难点与实战案例解析
开发语言·ios·swift
一楼的猫3 小时前
从工具链视角对比:番茄作家助手 vs 第三方写作辅助方案
java·服务器·开发语言·前端·学习·chatgpt·ai写作
程序leo源3 小时前
Qt窗口详解
开发语言·数据库·c++·qt·青少年编程·c#
likerhood3 小时前
Java static 关键字从浅入深
java·开发语言
猫猫的小茶馆4 小时前
【Python】函数与模块化编程
linux·开发语言·arm开发·驱动开发·python·stm32