8.1 继承(Inheritance)
8.1.1 为什么需要继承?
没有继承的问题:代码重复
csharp
// 学生类
class Student
{
public string Name { get; set; }
public int Age { get; set; }
public string StudentId { get; set; }
public void Eat() { Console.WriteLine($"{Name}正在吃饭"); }
public void Sleep() { Console.WriteLine($"{Name}正在睡觉"); }
public void Study() { Console.WriteLine($"{Name}正在学习"); }
}
// 老师类——大量重复代码!
class Teacher
{
public string Name { get; set; }
public int Age { get; set; }
public string TeacherId { get; set; }
public void Eat() { Console.WriteLine($"{Name}正在吃饭"); }
public void Sleep() { Console.WriteLine($"{Name}正在睡觉"); }
public void Teach() { Console.WriteLine($"{Name}正在教书"); }
}
使用继承:复用公共代码
csharp
// 基类(父类):包含公共的属性和方法
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public void Eat() { Console.WriteLine($"{Name}正在吃饭"); }
public void Sleep() { Console.WriteLine($"{Name}正在睡觉"); }
}
// 派生类(子类):继承 Person,并添加自己的成员
class Student : Person // : 表示继承
{
public string StudentId { get; set; }
public void Study() { Console.WriteLine($"{Name}正在学习"); }
}
class Teacher : Person
{
public string TeacherId { get; set; }
public void Teach() { Console.WriteLine($"{Name}正在教书"); }
}
// 使用
Student stu = new Student();
stu.Name = "张三"; // 来自 Person
stu.Age = 20; // 来自 Person
stu.StudentId = "001"; // 自己的
stu.Eat(); // 来自 Person
stu.Study(); // 自己的
8.1.2 继承的基本概念
text
┌─────────────────────────────────────────────────────────┐
│ Person │
│ (基类/父类/超类) │
│ ┌───────────────────────────────────────────────────┐ │
│ │ + Name : string │ │
│ │ + Age : int │ │
│ │ + Eat() │ │
│ │ + Sleep() │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
▲
│ 继承
┌───────────────┴───────────────┐
│ │
┌──────────────────────────┐ ┌──────────────────────────┐
│ Student │ │ Teacher │
│ (派生类/子类) │ │ (派生类/子类) │
│ ┌─────────────────────┐ │ │ ┌─────────────────────┐ │
│ │ + StudentId : string│ │ │ │ + TeacherId : string│ │
│ │ + Study() │ │ │ │ + Teach() │ │
│ └─────────────────────┘ │ │ └─────────────────────┘ │
└──────────────────────────┘ └──────────────────────────┘
术语:
-
基类(Base Class):被继承的类,也叫父类
-
派生类(Derived Class):继承的类,也叫子类
-
继承链:类可以有多层继承
8.1.3 继承的语法
csharp
// 基本语法
class 派生类 : 基类
{
// 新增成员
// 重写基类成员
}
// 示例
class Animal
{
public string Name { get; set; }
public void Eat() { Console.WriteLine($"{Name}在吃东西"); }
}
class Dog : Animal
{
public void Bark() { Console.WriteLine($"{Name}在汪汪叫"); }
}
class Puppy : Dog // 可以多层继承
{
public void Play() { Console.WriteLine($"{Name}在玩耍"); }
}
8.1.4 子类访问父类成员
csharp
class Vehicle
{
public string Brand { get; set; }
public int Speed { get; protected set; } // protected:子类可访问
public void Start()
{
Console.WriteLine("车辆启动");
}
private void SecretMethod() // private:子类不能访问
{
Console.WriteLine("这是秘密方法");
}
}
class Car : Vehicle
{
public int Doors { get; set; }
public void Accelerate()
{
Speed += 10; // ✅ 可以访问 protected 成员
Console.WriteLine($"{Brand}加速到{Speed}km/h");
Start(); // ✅ 可以访问 public 成员
// SecretMethod(); // ❌ private 成员不能访问
}
}
8.1.5 访问修饰符在继承中的影响
| 修饰符 | 基类内部 | 派生类内部 | 外部代码 |
|---|---|---|---|
public |
✅ | ✅ | ✅ |
protected |
✅ | ✅ | ❌ |
private |
✅ | ❌ | ❌ |
internal |
✅ | 同一程序集内可访问 | ❌ |
protected internal |
✅ | ✅(同一程序集或派生类) | ❌ |
csharp
class BaseClass
{
public int publicField;
protected int protectedField;
private int privateField;
internal int internalField;
protected internal int protectedInternalField;
void Test()
{
publicField = 1; // ✅
protectedField = 2; // ✅
privateField = 3; // ✅
internalField = 4; // ✅
protectedInternalField = 5; // ✅
}
}
class DerivedClass : BaseClass
{
void Test()
{
publicField = 1; // ✅
protectedField = 2; // ✅
// privateField = 3; // ❌
internalField = 4; // ✅(同一程序集)
protectedInternalField = 5; // ✅
}
}
8.1.6 构造函数与继承
重要规则:
-
创建子类对象时,会先调用父类的构造函数,再调用子类的构造函数
-
如果父类没有无参构造函数,子类必须显式调用父类的构造函数
csharp
class Animal
{
public string Name { get; set; }
// 无参构造函数
public Animal()
{
Console.WriteLine("Animal 无参构造函数被调用");
Name = "未知动物";
}
// 带参构造函数
public Animal(string name)
{
Console.WriteLine($"Animal 带参构造函数被调用,name={name}");
Name = name;
}
}
class Dog : Animal
{
public string Breed { get; set; }
// 隐式调用父类无参构造函数
public Dog()
{
Console.WriteLine("Dog 无参构造函数被调用");
Breed = "未知品种";
}
// 使用 base 关键字调用父类的带参构造函数
public Dog(string name, string breed) : base(name) // 先调用 Animal(name)
{
Console.WriteLine($"Dog 带参构造函数被调用,breed={breed}");
Breed = breed;
}
}
// 测试
Dog dog1 = new Dog();
// 输出:
// Animal 无参构造函数被调用
// Dog 无参构造函数被调用
Dog dog2 = new Dog("旺财", "金毛");
// 输出:
// Animal 带参构造函数被调用,name=旺财
// Dog 带参构造函数被调用,breed=金毛
父类没有无参构造函数时:
csharp
class Parent
{
public Parent(string message)
{
Console.WriteLine($"Parent: {message}");
}
// 没有无参构造函数
}
class Child : Parent
{
// 必须显式调用父类的构造函数
public Child(string message) : base(message)
{
Console.WriteLine("Child 构造函数");
}
// 或者调用父类的其他构造函数
public Child() : base("默认消息")
{
Console.WriteLine("Child 无参构造函数");
}
}
8.1.7 base 关键字
base 代表父类对象,用于访问父类的成员
csharp
class Animal
{
public string Name { get; set; }
public virtual void MakeSound()
{
Console.WriteLine("动物发出声音");
}
public Animal(string name)
{
Name = name;
}
}
class Cat : Animal
{
public string Color { get; set; }
// 调用父类构造函数
public Cat(string name, string color) : base(name)
{
Color = color;
}
// 调用父类方法
public override void MakeSound()
{
base.MakeSound(); // 先调用父类的方法
Console.WriteLine($"{Name}喵喵叫");
}
public void ShowInfo()
{
// 访问父类属性
Console.WriteLine($"名字:{base.Name},颜色:{Color}");
}
}
8.2 方法重写(Method Overriding)
8.2.1 为什么要重写?
子类需要改变从父类继承来的方法的行为
csharp
class Shape
{
public virtual double GetArea() // virtual:允许子类重写
{
return 0;
}
public virtual void Draw()
{
Console.WriteLine("绘制形状");
}
}
class Circle : Shape
{
public double Radius { get; set; }
public Circle(double radius)
{
Radius = radius;
}
public override double GetArea() // override:重写父类方法
{
return Math.PI * Radius * Radius;
}
public override void Draw()
{
Console.WriteLine($"绘制半径为{Radius}的圆");
}
}
class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public Rectangle(double width, double height)
{
Width = width;
Height = height;
}
public override double GetArea()
{
return Width * Height;
}
public override void Draw()
{
Console.WriteLine($"绘制{Width}x{Height}的矩形");
}
}
8.2.2 virtual 和 override
| 关键字 | 用途 | 说明 |
|---|---|---|
virtual |
父类中标记方法可以被重写 | 父类提供默认实现 |
override |
子类中重写父类的方法 | 提供新的实现 |
abstract |
标记抽象方法(本章后面讲) | 父类不提供实现 |
sealed |
阻止进一步重写 | 方法不能被重写 |
csharp
class Parent
{
public virtual void Method1()
{
Console.WriteLine("Parent.Method1");
}
public virtual void Method2()
{
Console.WriteLine("Parent.Method2");
}
public void Method3() // 没有 virtual,不能重写
{
Console.WriteLine("Parent.Method3");
}
}
class Child : Parent
{
public override void Method1() // 重写
{
Console.WriteLine("Child.Method1");
}
public override void Method2() // 重写
{
base.Method2(); // 可以调用父类实现
Console.WriteLine("Child.Method2");
}
// public override void Method3() // ❌ 错误!Method3 不是 virtual/abstract
// {
// }
public new void Method3() // new:隐藏父类方法(不是重写)
{
Console.WriteLine("Child.Method3");
}
}
8.2.3 重写 vs 隐藏
csharp
class Parent
{
public virtual void VirtualMethod()
{
Console.WriteLine("Parent.VirtualMethod");
}
public void NormalMethod()
{
Console.WriteLine("Parent.NormalMethod");
}
}
class Child : Parent
{
public override void VirtualMethod() // 重写
{
Console.WriteLine("Child.VirtualMethod");
}
public new void NormalMethod() // 隐藏(建议加上 new 关键字)
{
Console.WriteLine("Child.NormalMethod");
}
}
// 测试
Parent p = new Child();
p.VirtualMethod(); // 输出:Child.VirtualMethod(多态)
p.NormalMethod(); // 输出:Parent.NormalMethod(编译时决定)
Child c = new Child();
c.VirtualMethod(); // 输出:Child.VirtualMethod
c.NormalMethod(); // 输出:Child.NormalMethod
区别总结:
| 特性 | 重写(override) | 隐藏(new) |
|---|---|---|
父类方法需要 virtual |
是 | 否 |
| 通过父类引用调用 | 调用子类实现 | 调用父类实现 |
| 运行时行为 | 多态 | 编译时决定 |
8.3 多态(Polymorphism)
8.3.1 什么是多态?
多态 = 同一个方法在不同对象上有不同的表现形式
csharp
class Animal
{
public virtual void Speak()
{
Console.WriteLine("动物在叫");
}
}
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("汪汪汪");
}
}
class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("喵喵喵");
}
}
class Duck : Animal
{
public override void Speak()
{
Console.WriteLine("嘎嘎嘎");
}
}
// 多态的使用
Animal[] animals = new Animal[]
{
new Dog(),
new Cat(),
new Duck()
};
foreach (Animal animal in animals)
{
animal.Speak(); // 同一个方法调用,不同表现
}
// 输出:
// 汪汪汪
// 喵喵喵
// 嘎嘎嘎
8.3.2 多态的好处
csharp
// 没有多态:需要为每种动物写单独的方法
class AnimalSoundMaker
{
public void MakeDogSound(Dog dog) { dog.Speak(); }
public void MakeCatSound(Cat cat) { cat.Speak(); }
public void MakeDuckSound(Duck duck) { duck.Speak(); }
// 每增加一种动物,就要加一个方法
}
// 有多态:一个方法处理所有动物
class AnimalSoundMaker
{
public void MakeSound(Animal animal) // 参数是父类类型
{
animal.Speak(); // 运行时决定调用哪个子类的方法
}
}
// 使用
AnimalSoundMaker maker = new AnimalSoundMaker();
maker.MakeSound(new Dog()); // 汪汪汪
maker.MakeSound(new Cat()); // 喵喵喵
maker.MakeSound(new Duck()); // 嘎嘎嘎
8.3.3 多态的应用场景
场景1:统一处理不同类型对象
csharp
// 员工系统
class Employee
{
public string Name { get; set; }
public virtual double CalculateSalary()
{
return 3000; // 基本工资
}
}
class Manager : Employee
{
public double Bonus { get; set; }
public override double CalculateSalary()
{
return base.CalculateSalary() + Bonus;
}
}
class Salesperson : Employee
{
public double SalesAmount { get; set; }
public override double CalculateSalary()
{
return base.CalculateSalary() + SalesAmount * 0.05; // 5%提成
}
}
class Intern : Employee
{
public override double CalculateSalary()
{
return 1500; // 实习生工资
}
}
// 计算所有员工的工资
class PayrollSystem
{
public void CalculatePayroll(Employee[] employees)
{
double total = 0;
foreach (Employee emp in employees)
{
double salary = emp.CalculateSalary();
Console.WriteLine($"{emp.Name} 工资:{salary}元");
total += salary;
}
Console.WriteLine($"总工资支出:{total}元");
}
}
场景2:插件/可扩展架构
csharp
// 支付系统
abstract class PaymentMethod
{
public abstract void Pay(double amount);
}
class CreditCardPayment : PaymentMethod
{
public override void Pay(double amount)
{
Console.WriteLine($"使用信用卡支付{amount}元");
}
}
class AlipayPayment : PaymentMethod
{
public override void Pay(double amount)
{
Console.WriteLine($"使用支付宝支付{amount}元");
}
}
class WechatPayment : PaymentMethod
{
public override void Pay(double amount)
{
Console.WriteLine($"使用微信支付{amount}元");
}
}
class PaymentService
{
public void ProcessPayment(PaymentMethod method, double amount)
{
Console.WriteLine("开始处理支付...");
method.Pay(amount);
Console.WriteLine("支付完成");
}
}
// 使用
PaymentService service = new PaymentService();
service.ProcessPayment(new CreditCardPayment(), 100);
service.ProcessPayment(new AlipayPayment(), 200);
8.4 抽象类与抽象方法
8.4.1 什么是抽象类?
抽象类 = 不能被实例化的类,只能作为基类被继承
csharp
// 抽象类
abstract class Shape
{
// 抽象方法:没有方法体,子类必须实现
public abstract double GetArea();
// 抽象方法:没有方法体
public abstract double GetPerimeter();
// 普通方法:有方法体,子类可以继承
public void ShowInfo()
{
Console.WriteLine($"面积:{GetArea():F2},周长:{GetPerimeter():F2}");
}
}
// 抽象类不能被实例化
// Shape s = new Shape(); // ❌ 错误!不能创建抽象类的实例
// 具体类必须实现所有抽象方法
class Circle : Shape
{
public double Radius { get; set; }
public Circle(double radius)
{
Radius = radius;
}
public override double GetArea()
{
return Math.PI * Radius * Radius;
}
public override double GetPerimeter()
{
return 2 * Math.PI * Radius;
}
}
class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public Rectangle(double width, double height)
{
Width = width;
Height = height;
}
public override double GetArea()
{
return Width * Height;
}
public override double GetPerimeter()
{
return 2 * (Width + Height);
}
}
// 使用
Shape[] shapes = new Shape[]
{
new Circle(5),
new Rectangle(4, 6)
};
foreach (Shape shape in shapes)
{
shape.ShowInfo();
}
8.4.2 抽象类 vs 普通类
| 特点 | 普通类 | 抽象类 |
|---|---|---|
| 可以实例化 | ✅ | ❌ |
| 可以有抽象方法 | ❌ | ✅ |
| 可以作为基类 | ✅ | ✅ |
| 可以包含实现 | ✅ | ✅ |
8.4.3 抽象方法的规则
csharp
abstract class BaseClass
{
// 抽象方法不能有方法体
public abstract void AbstractMethod(); // ✅ 正确,没有 {}
// public abstract void WrongMethod() { } // ❌ 错误!抽象方法不能有实现
// 抽象方法不能是 private
// private abstract void PrivateMethod(); // ❌ 错误!
// 抽象方法可以是 protected 或 internal
protected abstract void ProtectedMethod();
internal abstract void InternalMethod();
// 可以同时包含普通方法和抽象方法
public void NormalMethod()
{
Console.WriteLine("普通方法");
}
}
class DerivedClass : BaseClass
{
// 必须实现所有抽象方法
public override void AbstractMethod()
{
Console.WriteLine("实现了抽象方法");
}
protected override void ProtectedMethod()
{
Console.WriteLine("实现了 protected 抽象方法");
}
internal override void InternalMethod()
{
Console.WriteLine("实现了 internal 抽象方法");
}
}
8.5 密封类与密封方法
8.5.1 密封类(sealed class)
密封类不能被继承
csharp
sealed class FinalClass
{
public void Method()
{
Console.WriteLine("这个方法不能被重写,这个类不能被继承");
}
}
// class Derived : FinalClass // ❌ 错误!不能继承密封类
// {
// }
什么时候用密封类:
-
类不需要被继承
-
为了安全性或性能考虑
-
.NET 基础库中的
string就是密封类
8.5.2 密封方法(sealed method)
密封方法阻止子类进一步重写
csharp
class Parent
{
public virtual void Method1()
{
Console.WriteLine("Parent.Method1");
}
public virtual void Method2()
{
Console.WriteLine("Parent.Method2");
}
}
class Child : Parent
{
public sealed override void Method1() // 密封这个方法
{
Console.WriteLine("Child.Method1");
}
public override void Method2()
{
Console.WriteLine("Child.Method2");
}
}
class GrandChild : Child
{
// public override void Method1() // ❌ 错误!Method1 已被密封
// {
// }
public override void Method2() // ✅ 可以重写 Method2
{
Console.WriteLine("GrandChild.Method2");
}
}
8.6 is 和 as 运算符
8.6.1 is 运算符------类型检查
is 检查对象是否是指定类型(或其派生类型)
csharp
class Animal { }
class Dog : Animal { }
class Cat : Animal { }
Animal animal = new Dog();
// 类型检查
bool isDog = animal is Dog; // true
bool isCat = animal is Cat; // false
bool isAnimal = animal is Animal; // true(Dog 是 Animal 的派生类)
// 结合声明(C# 7.0+)
if (animal is Dog dog)
{
Console.WriteLine($"这是一只狗:{dog}");
}
8.6.2 as 运算符------类型转换
as 尝试转换类型,失败时返回 null(不抛出异常)
csharp
object obj = "Hello";
// 使用 as 转换
string str = obj as string;
if (str != null)
{
Console.WriteLine($"转换成功:{str.Length}"); // 5
}
// 转换失败返回 null
object obj2 = 123;
string str2 = obj2 as string; // null
if (str2 == null)
{
Console.WriteLine("转换失败");
}
// as vs 强制转换
// 强制转换失败会抛异常
// string str3 = (string)obj2; // InvalidCastException
// as 转换失败返回 null,不抛异常
string str4 = obj2 as string; // null,安全
8.6.3 类型转换总结
csharp
// 向上转型(隐式):子类 → 父类
Dog dog = new Dog();
Animal animal = dog; // 自动转换
// 向下转型(显式):父类 → 子类
Animal animal2 = new Dog();
// 方式1:强制转换(可能抛异常)
Dog dog1 = (Dog)animal2;
// 方式2:as 转换(失败返回 null)
Dog dog2 = animal2 as Dog;
if (dog2 != null) { }
// 方式3:is + 强制转换
if (animal2 is Dog dog3)
{
// dog3 可用
}
8.7 综合示例
示例1:图形绘制系统
csharp
using System;
using System.Collections.Generic;
// 抽象基类
abstract class Shape
{
public string Color { get; set; }
protected Shape(string color)
{
Color = color;
}
// 抽象方法
public abstract double GetArea();
public abstract double GetPerimeter();
public abstract void Draw();
// 虚方法
public virtual void ShowInfo()
{
Console.WriteLine($"形状类型:{GetType().Name}");
Console.WriteLine($"颜色:{Color}");
Console.WriteLine($"面积:{GetArea():F2}");
Console.WriteLine($"周长:{GetPerimeter():F2}");
}
}
// 圆形
class Circle : Shape
{
public double Radius { get; set; }
public Circle(double radius, string color) : base(color)
{
Radius = radius;
}
public override double GetArea() => Math.PI * Radius * Radius;
public override double GetPerimeter() => 2 * Math.PI * Radius;
public override void Draw()
{
Console.WriteLine($"绘制一个{Color}的圆,半径:{Radius}");
}
}
// 矩形
class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public Rectangle(double width, double height, string color) : base(color)
{
Width = width;
Height = height;
}
public override double GetArea() => Width * Height;
public override double GetPerimeter() => 2 * (Width + Height);
public override void Draw()
{
Console.WriteLine($"绘制一个{Color}的矩形,{Width}x{Height}");
}
}
// 三角形
class Triangle : Shape
{
public double SideA { get; set; }
public double SideB { get; set; }
public double SideC { get; set; }
public Triangle(double a, double b, double c, string color) : base(color)
{
SideA = a;
SideB = b;
SideC = c;
}
public override double GetArea()
{
// 海伦公式
double s = (SideA + SideB + SideC) / 2;
return Math.Sqrt(s * (s - SideA) * (s - SideB) * (s - SideC));
}
public override double GetPerimeter() => SideA + SideB + SideC;
public override void Draw()
{
Console.WriteLine($"绘制一个{Color}的三角形,边长:{SideA},{SideB},{SideC}");
}
}
// 正方形(继承自矩形)
class Square : Rectangle
{
public Square(double side, string color) : base(side, side, color)
{
}
public override void Draw()
{
Console.WriteLine($"绘制一个{Color}的正方形,边长:{Width}");
}
}
// 绘图程序
class DrawingProgram
{
private List<Shape> shapes = new List<Shape>();
public void AddShape(Shape shape)
{
shapes.Add(shape);
Console.WriteLine($"已添加{shape.GetType().Name}");
}
public void DrawAll()
{
Console.WriteLine("\n=== 绘制所有图形 ===");
foreach (Shape shape in shapes)
{
shape.Draw();
}
}
public void ShowAllInfo()
{
Console.WriteLine("\n=== 所有图形信息 ===");
foreach (Shape shape in shapes)
{
shape.ShowInfo();
Console.WriteLine();
}
}
public double GetTotalArea()
{
double total = 0;
foreach (Shape shape in shapes)
{
total += shape.GetArea();
}
return total;
}
public void FilterByColor(string color)
{
Console.WriteLine($"\n=== {color}色的图形 ===");
foreach (Shape shape in shapes)
{
if (shape.Color.Equals(color, StringComparison.OrdinalIgnoreCase))
{
shape.Draw();
}
}
}
}
class Program
{
static void Main()
{
DrawingProgram program = new DrawingProgram();
// 添加各种形状
program.AddShape(new Circle(5, "红色"));
program.AddShape(new Rectangle(10, 6, "蓝色"));
program.AddShape(new Triangle(3, 4, 5, "绿色"));
program.AddShape(new Square(7, "黄色"));
program.AddShape(new Circle(3, "红色"));
// 绘制所有
program.DrawAll();
// 显示信息
program.ShowAllInfo();
// 总面积
Console.WriteLine($"\n所有图形总面积:{program.GetTotalArea():F2}");
// 按颜色筛选
program.FilterByColor("红色");
}
}
示例2:员工工资管理系统
csharp
using System;
using System.Collections.Generic;
// 员工抽象类
abstract class Employee
{
public string Id { get; set; }
public string Name { get; set; }
public DateTime HireDate { get; set; }
protected Employee(string id, string name, DateTime hireDate)
{
Id = id;
Name = name;
HireDate = hireDate;
}
// 抽象方法:计算工资
public abstract double CalculateSalary();
// 虚方法:计算工龄工资
public virtual double CalculateSeniorityBonus()
{
int years = DateTime.Now.Year - HireDate.Year;
if (DateTime.Now.DayOfYear < HireDate.DayOfYear) years--;
return years * 100; // 每年100元工龄工资
}
// 显示信息
public virtual void ShowInfo()
{
Console.WriteLine($"ID:{Id},姓名:{Name},入职日期:{HireDate:yyyy-MM-dd}");
Console.WriteLine($"工资:{CalculateSalary():C},工龄工资:{CalculateSeniorityBonus():C}");
}
}
// 全职员工
class FullTimeEmployee : Employee
{
public double BaseSalary { get; set; }
public double Bonus { get; set; }
public FullTimeEmployee(string id, string name, DateTime hireDate,
double baseSalary, double bonus)
: base(id, name, hireDate)
{
BaseSalary = baseSalary;
Bonus = bonus;
}
public override double CalculateSalary()
{
return BaseSalary + Bonus + CalculateSeniorityBonus();
}
public override void ShowInfo()
{
Console.WriteLine($"\n[全职员工]");
base.ShowInfo();
Console.WriteLine($"基本工资:{BaseSalary:C},奖金:{Bonus:C}");
}
}
// 兼职员工(按时计薪)
class PartTimeEmployee : Employee
{
public double HourlyRate { get; set; }
public double HoursWorked { get; set; }
public PartTimeEmployee(string id, string name, DateTime hireDate,
double hourlyRate, double hoursWorked)
: base(id, name, hireDate)
{
HourlyRate = hourlyRate;
HoursWorked = hoursWorked;
}
public override double CalculateSalary()
{
return HourlyRate * HoursWorked + CalculateSeniorityBonus();
}
public override void ShowInfo()
{
Console.WriteLine($"\n[兼职员工]");
base.ShowInfo();
Console.WriteLine($"时薪:{HourlyRate:C},工时:{HoursWorked}小时");
}
}
// 销售员工(底薪+提成)
class SalesEmployee : Employee
{
public double BaseSalary { get; set; }
public double SalesAmount { get; set; }
public double CommissionRate { get; set; } // 提成比例
public SalesEmployee(string id, string name, DateTime hireDate,
double baseSalary, double commissionRate)
: base(id, name, hireDate)
{
BaseSalary = baseSalary;
CommissionRate = commissionRate;
SalesAmount = 0;
}
public void RecordSales(double amount)
{
SalesAmount += amount;
Console.WriteLine($"{Name} 销售业绩 +{amount:C},总业绩:{SalesAmount:C}");
}
public override double CalculateSalary()
{
return BaseSalary + SalesAmount * CommissionRate + CalculateSeniorityBonus();
}
public override void ShowInfo()
{
Console.WriteLine($"\n[销售员工]");
base.ShowInfo();
Console.WriteLine($"底薪:{BaseSalary:C},提成比例:{CommissionRate:P0}");
Console.WriteLine($"当前业绩:{SalesAmount:C},业绩提成:{SalesAmount * CommissionRate:C}");
}
}
// 实习生(没有工龄工资)
class Intern : Employee
{
public double Stipend { get; set; } // 津贴
public Intern(string id, string name, DateTime hireDate, double stipend)
: base(id, name, hireDate)
{
Stipend = stipend;
}
public override double CalculateSalary()
{
return Stipend; // 实习生没有工龄工资
}
// 重写工龄工资方法
public override double CalculateSeniorityBonus()
{
return 0; // 实习生没有工龄工资
}
public override void ShowInfo()
{
Console.WriteLine($"\n[实习生]");
base.ShowInfo();
Console.WriteLine($"津贴:{Stipend:C}");
}
}
// 工资管理系统
class PayrollSystem
{
private List<Employee> employees = new List<Employee>();
public void AddEmployee(Employee emp)
{
employees.Add(emp);
Console.WriteLine($"已添加员工:{emp.Name}");
}
public void ProcessPayroll()
{
Console.WriteLine("\n" + new string('=', 50));
Console.WriteLine("开始处理工资...");
Console.WriteLine(new string('=', 50));
double totalSalary = 0;
foreach (Employee emp in employees)
{
emp.ShowInfo();
totalSalary += emp.CalculateSalary();
Console.WriteLine();
}
Console.WriteLine(new string('-', 50));
Console.WriteLine($"总工资支出:{totalSalary:C}");
Console.WriteLine(new string('=', 50));
}
public void ShowEmployeeByType<T>() where T : Employee
{
Console.WriteLine($"\n=== {typeof(T).Name} 列表 ===");
foreach (Employee emp in employees)
{
if (emp is T)
{
Console.WriteLine($"{emp.Id} - {emp.Name}");
}
}
}
public double GetTotalSalary()
{
double total = 0;
foreach (Employee emp in employees)
{
total += emp.CalculateSalary();
}
return total;
}
}
class Program
{
static void Main()
{
PayrollSystem payroll = new PayrollSystem();
// 添加各种类型员工
payroll.AddEmployee(new FullTimeEmployee("E001", "张三",
new DateTime(2020, 1, 15), 8000, 2000));
payroll.AddEmployee(new PartTimeEmployee("E002", "李四",
new DateTime(2023, 6, 1), 50, 80));
SalesEmployee sales = new SalesEmployee("E003", "王五",
new DateTime(2021, 3, 10), 5000, 0.05);
sales.RecordSales(100000);
payroll.AddEmployee(sales);
payroll.AddEmployee(new Intern("E004", "赵六",
new DateTime(2024, 1, 1), 2000));
// 处理工资
payroll.ProcessPayroll();
// 按类型统计
payroll.ShowEmployeeByType<FullTimeEmployee>();
payroll.ShowEmployeeByType<SalesEmployee>();
}
}
示例3:简单游戏角色系统
csharp
using System;
// 角色基类
abstract class Character
{
public string Name { get; set; }
public int Health { get; protected set; }
public int MaxHealth { get; protected set; }
public int Level { get; protected set; }
public bool IsAlive => Health > 0;
protected Character(string name, int maxHealth)
{
Name = name;
MaxHealth = maxHealth;
Health = maxHealth;
Level = 1;
}
// 抽象攻击方法
public abstract int Attack();
// 受伤害方法
public virtual void TakeDamage(int damage)
{
Health -= damage;
if (Health < 0) Health = 0;
Console.WriteLine($"{Name} 受到 {damage} 点伤害,剩余生命:{Health}/{MaxHealth}");
if (!IsAlive)
{
Console.WriteLine($"{Name} 已经倒下!");
}
}
// 治疗
public virtual void Heal(int amount)
{
Health += amount;
if (Health > MaxHealth) Health = MaxHealth;
Console.WriteLine($"{Name} 恢复了 {amount} 点生命,当前生命:{Health}/{MaxHealth}");
}
// 升级
public virtual void LevelUp()
{
Level++;
MaxHealth += 20;
Health = MaxHealth;
Console.WriteLine($"{Name} 升级到 {Level} 级!最大生命增加到 {MaxHealth}");
}
// 显示状态
public virtual void ShowStatus()
{
Console.WriteLine($"[{GetType().Name}] {Name} | Lv.{Level} | HP: {Health}/{MaxHealth}");
}
}
// 战士
class Warrior : Character
{
public int Strength { get; set; }
public Warrior(string name) : base(name, 120)
{
Strength = 15;
}
public override int Attack()
{
int damage = Strength + Level * 2;
Console.WriteLine($"{Name} 使用【猛击】,造成 {damage} 点伤害!");
return damage;
}
// 战士特有技能:盾墙(减伤)
public void ShieldWall()
{
Console.WriteLine($"{Name} 举盾防御,接下来受到的伤害减半!");
// 简化实现,实际可以用 Buff 系统
}
public override void LevelUp()
{
base.LevelUp();
Strength += 5;
Console.WriteLine($"力量提升到 {Strength}");
}
}
// 法师
class Mage : Character
{
public int Intelligence { get; set; }
public int Mana { get; set; }
public Mage(string name) : base(name, 80)
{
Intelligence = 18;
Mana = 100;
}
public override int Attack()
{
int damage = Intelligence + Level * 3;
if (Mana >= 20)
{
Mana -= 20;
Console.WriteLine($"{Name} 施放【火球术】,造成 {damage} 点伤害!(剩余法力:{Mana})");
return damage;
}
else
{
Console.WriteLine($"{Name} 法力不足,使用法杖敲击,造成 5 点伤害!");
return 5;
}
}
// 法师特有技能:冥想(回复法力)
public void Meditate()
{
Mana += 30;
if (Mana > 100) Mana = 100;
Console.WriteLine($"{Name} 进入冥想,回复30点法力,当前法力:{Mana}");
}
public override void LevelUp()
{
base.LevelUp();
Intelligence += 4;
Mana = 100;
Console.WriteLine($"智力提升到 {Intelligence},法力回满");
}
}
// 弓箭手
class Archer : Character
{
public int Dexterity { get; set; }
public int Arrows { get; set; }
public Archer(string name) : base(name, 100)
{
Dexterity = 14;
Arrows = 30;
}
public override int Attack()
{
int damage = Dexterity + Level * 2;
if (Arrows > 0)
{
Arrows--;
Console.WriteLine($"{Name} 射出一箭,造成 {damage} 点伤害!(剩余箭矢:{Arrows})");
return damage;
}
else
{
Console.WriteLine($"{Name} 箭矢用尽,无法攻击!");
return 0;
}
}
// 弓箭手特有技能:装填箭矢
public void Reload()
{
Arrows += 10;
Console.WriteLine($"{Name} 装填了10支箭矢,当前箭矢:{Arrows}");
}
public override void LevelUp()
{
base.LevelUp();
Dexterity += 4;
Arrows += 5;
Console.WriteLine($"敏捷提升到 {Dexterity},箭矢上限+5");
}
}
// 战斗系统
class BattleSystem
{
public void Fight(Character attacker, Character defender)
{
Console.WriteLine($"\n>>> {attacker.Name} 攻击 {defender.Name}!");
int damage = attacker.Attack();
defender.TakeDamage(damage);
}
public void TeamBattle(Character[] team1, Character[] team2)
{
Console.WriteLine("\n" + new string('=', 50));
Console.WriteLine("团队战斗开始!");
Console.WriteLine(new string('=', 50));
Random random = new Random();
while (IsTeamAlive(team1) && IsTeamAlive(team2))
{
// 随机选择攻击者和防御者
Character attacker = GetRandomAlive(team1, team2, random);
Character defender = GetRandomAlive(team2, team1, random);
if (attacker != null && defender != null)
{
Fight(attacker, defender);
}
// 每轮结束后显示状态
Console.WriteLine("\n当前状态:");
ShowTeamStatus(team1);
ShowTeamStatus(team2);
Console.WriteLine(new string('-', 30));
System.Threading.Thread.Sleep(1000);
}
// 宣布胜者
if (IsTeamAlive(team1))
Console.WriteLine("\n🏆 队伍1获胜!");
else
Console.WriteLine("\n🏆 队伍2获胜!");
}
private bool IsTeamAlive(Character[] team)
{
foreach (Character c in team)
{
if (c.IsAlive) return true;
}
return false;
}
private Character GetRandomAlive(Character[] attackers, Character[] defenders, Random random)
{
List<Character> alive = new List<Character>();
foreach (Character c in attackers)
{
if (c.IsAlive) alive.Add(c);
}
if (alive.Count == 0) return null;
return alive[random.Next(alive.Count)];
}
private void ShowTeamStatus(Character[] team)
{
foreach (Character c in team)
{
c.ShowStatus();
}
}
}
class Program
{
static void Main()
{
// 创建角色
Warrior warrior = new Warrior("战神");
Mage mage = new Mage("大法师");
Archer archer = new Archer("神射手");
// 显示初始状态
Console.WriteLine("=== 角色创建 ===");
warrior.ShowStatus();
mage.ShowStatus();
archer.ShowStatus();
Console.WriteLine("\n=== 升级测试 ===");
warrior.LevelUp();
mage.LevelUp();
Console.WriteLine("\n=== 技能测试 ===");
warrior.ShieldWall();
mage.Meditate();
archer.Reload();
Console.WriteLine("\n=== 战斗测试 ===");
BattleSystem battle = new BattleSystem();
battle.Fight(warrior, mage);
battle.Fight(mage, warrior);
// 团队战斗
Console.WriteLine("\n=== 团队战斗测试 ===");
Character[] team1 = new Character[]
{
new Warrior("战士A"),
new Mage("法师A")
};
Character[] team2 = new Character[]
{
new Archer("弓箭手B"),
new Warrior("战士B")
};
battle.TeamBattle(team1, team2);
}
}
8.8 常见错误与陷阱
错误1:忘记在子类中实现抽象方法
csharp
abstract class Base
{
public abstract void MustImplement();
}
class Derived : Base
{
// 没有实现 MustImplement() // ❌ 编译错误
}
错误2:sealed 类被继承
csharp
sealed class Final { }
class Derived : Final // ❌ 错误
{
}
错误3:base 调用错误
csharp
class Parent
{
public Parent(int x) { }
}
class Child : Parent
{
// 没有调用父类构造函数 // ❌ 错误
public Child() { }
}
错误4:is 和 as 使用混乱
csharp
object obj = "123";
int num = obj as int; // ❌ int 是值类型,不能用于 as
// 值类型应使用 is + 强制转换
if (obj is int)
{
int num = (int)obj;
}
错误5:构造函数中调用虚方法
csharp
class Base
{
public Base()
{
VirtualMethod(); // ⚠️ 危险!可能调用子类未完全初始化的方法
}
public virtual void VirtualMethod() { }
}
8.9 本章总结
核心知识点导图
text
面向对象编程(进阶)
├── 继承
│ ├── 语法:class 子类 : 父类
│ ├── base 关键字:访问父类成员
│ ├── 构造函数调用链
│ └── 访问修饰符的影响
│
├── 多态
│ ├── virtual + override
│ ├── 通过父类引用调用子类方法
│ └── 提高代码扩展性
│
├── 抽象类与抽象方法
│ ├── abstract 关键字
│ ├── 不能实例化
│ └── 子类必须实现抽象方法
│
├── 密封类与方法
│ ├── sealed class:不能被继承
│ └── sealed override:阻止进一步重写
│
└── 类型转换
├── is:类型检查
├── as:安全转换(引用类型)
└── 强制转换:(类型)对象
继承 vs 接口(预告)
| 特性 | 继承类 | 实现接口(第九章) |
|---|---|---|
| 可以继承多个 | ❌ | ✅ |
| 可以提供实现 | ✅ | ❌(默认实现除外) |
| 可以包含字段 | ✅ | ❌ |
| 可以包含构造函数 | ✅ | ❌ |