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接口规范数据行为 - 多个实体类实现接口
- 链表存储接口类型,实现多态存放不同对象
- 遍历链表时自动调用各自重写的方法
更多推荐
所有评论(0)