Files
My-Notes/笔记/CSharp/核心/抽象类和接口的区别.md
2026-06-21 09:51:55 +08:00

6.9 KiB

抽象类和接口的区别

概念: 抽象类和接口都可以用来做抽象,都能配合多态使用 但它们表达的语义不一样

简单理解: 抽象类更像“是什么” 接口更像“能做什么”

例如:

  1. Animal 抽象类:表示狗、猫、鸟都是动物
  2. IFlyable 接口:表示某个对象具备飞行能力

先看抽象类

抽象类:

abstract class Animal
{
	public string Name;

	public Animal(string name)
	{
		Name = name;
	}

	public void Eat()
	{
		Console.WriteLine(Name + "吃东西");
	}

	public abstract void Speak();
}

子类:

class Dog : Animal
{
	public Dog(string name) : base(name)
	{
	}

	public override void Speak()
	{
		Console.WriteLine(Name + "汪汪");
	}
}

抽象类适合:

  1. 有明确父子关系
  2. 要复用公共字段、构造函数、普通方法
  3. 还要强制子类实现某些行为

一句话: 抽象类适合表达“这类东西的共同基础”


再看接口

接口:

interface IFlyable
{
	void Fly();
}

实现类:

class Bird : IFlyable
{
	public void Fly()
	{
		Console.WriteLine("鸟飞");
	}
}

class Plane : IFlyable
{
	public void Fly()
	{
		Console.WriteLine("飞机飞");
	}
}

接口适合:

  1. 表达一种能力
  2. 多个无关类型都能实现同一种行为
  3. 一个类需要同时拥有多种能力
  4. 调用方只关心能力,不关心具体身份

一句话: 接口适合表达“谁能做这件事”


核心区别

对比点 抽象类 接口
关键词 abstract class interface
主要表达 是什么 能做什么
继承数量 一个类只能继承一个类 一个类可以实现多个接口
成员实现 可以有普通实现,也可以有抽象成员 主要规定契约,要求实现类提供成员
字段 可以有字段 通常不放实例字段
构造函数 可以有构造函数 不能像类一样写实例构造函数
适合场景 提取共同父类和公共代码 表达能力、规则、插件式扩展
多态使用 可以用抽象类引用指向子类 可以用接口引用指向实现类

单继承和多实现

C# 中: 一个类只能继承一个父类 但可以实现多个接口

abstract class Character
{
	public string Name;
}

interface IMovable
{
	void Move();
}

interface IAttackable
{
	void Attack();
}

class Player : Character, IMovable, IAttackable
{
	public void Move()
	{
		Console.WriteLine("移动");
	}

	public void Attack()
	{
		Console.WriteLine("攻击");
	}
}

理解:

  1. Player 是一种 Character
  2. Player 同时具备移动能力和攻击能力
  3. 父类写一个,接口可以写多个

什么时候用抽象类

适合用抽象类:

abstract class Enemy
{
	public int Hp;

	public void TakeDamage(int damage)
	{
		Hp -= damage;
	}

	public abstract void Attack();
}

因为:

  1. 小怪、Boss、远程敌人都可以算敌人
  2. 它们都有血量、受伤逻辑
  3. 它们攻击方式不同

这种情况抽象类很合适: 有共同身份,也有公共代码


什么时候用接口

适合用接口:

interface IDamageable
{
	void TakeDamage(int damage);
}

实现者可能是:

class Player : IDamageable
{
	public void TakeDamage(int damage)
	{
	}
}

class Enemy : IDamageable
{
	public void TakeDamage(int damage)
	{
	}
}

class Box : IDamageable
{
	public void TakeDamage(int damage)
	{
	}
}

玩家、敌人、箱子不是同一种父类关系,但它们都可以“受伤/受击”。

这种情况接口更合适: 没有共同身份,但有共同能力


可以一起用

抽象类和接口不是二选一,经常会一起使用。

abstract class Character
{
	public string Name;

	public Character(string name)
	{
		Name = name;
	}

	public abstract void Update();
}

interface IAttackable
{
	void Attack();
}

class Warrior : Character, IAttackable
{
	public Warrior(string name) : base(name)
	{
	}

	public override void Update()
	{
		Console.WriteLine(Name + "更新状态");
	}

	public void Attack()
	{
		Console.WriteLine(Name + "挥剑攻击");
	}
}

理解:

  1. Warrior 是一种 Character
  2. Warrior 具备 IAttackable 能力
  3. 抽象类负责共同身份和基础逻辑
  4. 接口负责额外能力

和多态的关系

抽象类多态:

Animal animal = new Dog();
animal.Speak();

接口多态:

IFlyable flyable = new Bird();
flyable.Fly();

它们都能做到: 用统一类型接收不同具体对象 运行时执行不同实现

区别在于:

  1. 抽象类变量强调“它属于某个父类体系”
  2. 接口变量强调“它拥有某个能力”

常见判断方法

问自己几个问题:

  1. 这些类是不是同一种东西?
    • 是:优先考虑抽象类
    • 不是:优先考虑接口
  2. 是否需要复用字段、构造函数、普通方法?
    • 是:抽象类更合适
  3. 一个类是否需要同时拥有多种能力?
    • 是:接口更合适
  4. 调用方真正关心的是身份还是能力?
    • 关心身份:抽象类
    • 关心能力:接口

常见误区

误区1:接口就是更高级的抽象类

不是。 接口和抽象类不是上下级关系,而是用途不同


误区2:有公共方法就一定用抽象类

不一定。 如果这些类型没有真正的父子关系,硬抽象成父类可能违反里氏替换原则


误区3:接口只能有方法

接口也可以有属性、索引器、事件等成员。 初学阶段先重点掌握方法和属性即可。


注意

  1. 抽象类和接口都不能直接创建对象
  2. 抽象类用 abstract class,接口用 interface
  3. 抽象类适合表达共同身份和公共代码
  4. 接口适合表达能力和契约
  5. 类只能继承一个父类,但可以实现多个接口
  6. 抽象类和接口都可以配合多态
  7. 设计时先看语义,不要只看语法

一句话记忆

==抽象类管“是谁的一种”,接口管“能做什么”。==

再压缩一点: ==同类用抽象类,能力用接口。==


面试/复习时怎么说

可以这样答:

“抽象类和接口都能表达抽象,也都可以配合多态使用。
抽象类更强调父子关系和公共代码复用,一个类只能继承一个抽象类;接口更强调能力或契约,一个类可以实现多个接口。
如果多个类本质上是同一种东西,并且有公共字段、构造函数或普通方法,可以用抽象类;如果只是多个类型都有同一种能力,通常用接口更合适。”


引用:

  1. Microsoft Learn: abstract (C# Reference)
  2. Microsoft Learn: interface (C# Reference)
  3. Microsoft Learn: Interfaces

#抽象类 #接口 #核心