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)—— 蓝图/模板
│ ├── 字段 —— 存储数据
│ ├── 方法 —— 定义行为
│