C# 抽象类、接口、多态、单向链表 完整讲解 + 代码示例
一、抽象类 abstract class
1. 核心特点
- 用
abstract修饰,不能实例化,只能被继承 - 可包含:普通字段、普通方法、抽象方法(无方法体)、构造函数、属性
- 派生类必须重写所有抽象成员,用
override - 单继承:一个类只能继承一个抽象类
- 有构造函数,子类实例化时会先执行父抽象类构造
示例代码
csharp
运行
// 抽象动物父类 abstract class Animal { public string Name { get; set; } // 普通构造 public Animal(string name) { Name = name; } // 抽象方法:无实现,子类必须重写 public abstract void Speak(); // 普通方法:已有实现,子类可重写可不重写 public void Run() { Console.WriteLine($"{Name} 在奔跑"); } } // 子类继承抽象类,重写抽象方法 class Dog : Animal { public Dog(string name) : base(name) { } public override void Speak() { Console.WriteLine($"{Name}:汪汪汪"); } } class Cat : Animal { public Cat(string name) : base(name) { } public override void Speak() { Console.WriteLine($"{Name}:喵喵喵"); } }二、接口 interface
1. 核心特点
- 关键字
interface,不能实例化、无构造函数 - 只能包含:方法、属性、事件、索引器(不能有字段、普通变量)
- 默认所有成员都是
public,不能加访问修饰符 - 一个类可实现多个接口(解决单继承局限)
- 接口成员必须全部实现,用
public重写 - C#8.0+ 接口可以写默认实现方法
示例代码
csharp
运行
// 会叫的接口 interface ISpeak { void Speak(); } // 会飞的接口 interface IFly { void Fly(); } // 鸟类:同时实现两个接口 class Bird : ISpeak, IFly { public void Speak() { Console.WriteLine("叽叽叽"); } public void Fly() { Console.WriteLine("小鸟展翅飞翔"); } }抽象类 vs 接口对比
表格
| 特性 | 抽象类 abstract | 接口 interface |
|---|---|---|
| 继承 / 实现 | 单继承 | 多实现 |
| 字段 | 可以有成员变量 | 不能有字段 |
| 构造函数 | 有 | 无 |
| 访问修饰 | 可 private/protected/public | 只能 public |
| 使用场景 | 提取子类公共代码、有共同属性行为 | 定义一套行为规范,多类不相关也可实现 |
三、多态 Polymorphism
1. 两种多态
- 编译时多态(静态):方法重载 Overload,同一个类同名不同参数
- 运行时多态(动态,核心):方法重写 Override,父类引用指向子类对象
动态多态核心规则
- 父类变量 = new 子类 ();
- 调用虚 / 抽象方法时,执行子类重写后的逻辑
多态完整演示(结合上面抽象类)
csharp
运行
class Program { static void Main() { // 父类引用指向不同子类,多态体现 Animal a1 = new Dog("旺财"); Animal a2 = new Cat("橘猫"); TestSpeak(a1); TestSpeak(a2); } // 统一接收父类,自动执行子类方法 static void TestSpeak(Animal animal) { animal.Speak(); animal.Run(); } }输出:
plaintext
旺财:汪汪汪 旺财 在奔跑 橘猫:喵喵喵 橘猫 在奔跑四、单向链表(手写基础链表)
链表核心:节点 Node + 链表管理类
- 节点:存储数据 + 下一个节点地址
- 链表:记录头节点,提供增删改查
完整单向链表代码
csharp
运行
// 链表节点 class Node<T> { public T Data; // 存储数据 public Node<T> Next; // 指向下一个节点 public Node(T data) { Data = data; Next = null; } } // 泛型单向链表 class SingleLinkList<T> { private Node<T> head; // 头节点 public int Count { get; private set; } // 节点数量 // 尾部添加 public void Add(T data) { Node<T> newNode = new Node<T>(data); if (head == null) { head = newNode; } else { Node<T> temp = head; while (temp.Next != null) { temp = temp.Next; } temp.Next = newNode; } Count++; } // 遍历打印所有元素 public void Show() { if (head == null) { Console.WriteLine("链表为空"); return; } Node<T> temp = head; while (temp != null) { Console.Write(temp.Data + " "); temp = temp.Next; } Console.WriteLine(); } // 根据值删除第一个匹配节点 public void Remove(T data) { if (head == null) return; // 头节点就是目标 if (head.Data.Equals(data)) { head = head.Next; Count--; return; } Node<T> temp = head; // 找到待删节点的前一个 while (temp.Next != null && !temp.Next.Data.Equals(data)) { temp = temp.Next; } if (temp.Next == null) return; temp.Next = temp.Next.Next; Count--; } } // 测试 class Program { static void Main() { SingleLinkList<int> list = new SingleLinkList<int>(); list.Add(10); list.Add(20); list.Add(30); list.Show(); // 10 20 30 list.Remove(20); list.Show(); // 10 30 Console.WriteLine("节点总数:" + list.Count); } }五、综合拓展:接口 + 多态 + 链表结合思路
- 定义
IData接口规范数据行为 - 多个实体类实现接口
- 链表存储接口类型,实现多态存放不同对象
- 遍历链表时自动调用各自重写的方法
