10 KiB
概念:
使用 abstract 关键字修饰的类,叫做==抽象类==
使用 abstract 关键字修饰的方法,叫做==抽象方法==
简单理解: 抽象类像一份“不完整的父类模板” 它可以规定子类必须有什么能力,但具体怎么做,交给子类自己实现
补充: 有些教程会把“抽象方法”叫作“抽象函数”,初学阶段可以先理解成同一个意思
通常用法:
语法
抽象类:
abstract class 类名
{
}
抽象方法:
abstract class 类名
{
public abstract void 方法名();
}
子类实现抽象方法:
class 子类名 : 抽象类名
{
public override void 方法名()
{
}
}
注意:
抽象方法没有方法体,结尾直接写分号 ;
基础案例
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 + ":汪汪");
}
}
class Cat : Animal
{
public Cat(string name) : base(name)
{
}
public override void Speak()
{
Console.WriteLine(Name + ":喵喵");
}
}
Animal a1 = new Dog("旺财");
Animal a2 = new Cat("小花");
a1.Eat();
a1.Speak();
a2.Eat();
a2.Speak();
理解:
Animal是抽象类,不能直接new Animal()Eat()是普通方法,子类可以直接继承使用Speak()是抽象方法,父类只规定“动物要会叫”Dog和Cat必须用override实现自己的Speak()Animal a1 = new Dog()可以配合多态使用
抽象类不能直接实例化
错误写法:
abstract class Animal
{
public abstract void Speak();
}
Animal animal = new Animal();
原因: 抽象类是不完整的类型,里面可能有没实现的抽象成员 所以不能直接创建对象
正确用法:
Animal animal = new Dog("旺财");
animal.Speak();
抽象方法必须在子类中实现
如果子类不是抽象类,就必须实现父类中所有抽象方法。
abstract class Shape
{
public abstract double GetArea();
}
class Circle : Shape
{
public double Radius;
public Circle(double radius)
{
Radius = radius;
}
public override double GetArea()
{
return 3.14 * Radius * Radius;
}
}
错误写法:
class Circle : Shape
{
}
原因:
Circle 是普通类,但没有实现 Shape 要求的 GetArea()
子类也可以继续抽象
如果子类暂时不想实现所有抽象方法,那么这个子类也必须声明成 abstract。
abstract class Shape
{
public abstract double GetArea();
public abstract double GetLength();
}
abstract class RoundShape : Shape
{
public override double GetLength()
{
return 0;
}
// GetArea 暂时不实现,所以 RoundShape 也必须是抽象类
}
理解: 普通类必须把抽象成员补完整 抽象类可以继续把没补完的内容留给下一级子类
抽象类里可以有什么
抽象类不只是能写抽象方法,也可以写普通成员。
abstract class Character
{
public string Name;
public int Hp;
public Character(string name, int hp)
{
Name = name;
Hp = hp;
}
public void TakeDamage(int damage)
{
Hp -= damage;
Console.WriteLine(Name + "受到了" + damage + "点伤害");
}
public virtual void Move()
{
Console.WriteLine(Name + "正在移动");
}
public abstract void Attack();
}
抽象类中可以有:
- 成员变量
- 成员属性
- 构造函数
- 普通方法
- 虚方法
- 抽象方法
- 抽象属性、抽象索引器、抽象事件
一句话: 抽象类可以有“已经写好的公共功能”,也可以有“要求子类必须补上的功能”
抽象类可以有构造函数
抽象类不能直接 new,但它仍然可以有构造函数。
原因: 创建子类对象时,会先构造父类那一部分
abstract class Enemy
{
public string Name;
public Enemy(string name)
{
Name = name;
Console.WriteLine("Enemy构造");
}
public abstract void Attack();
}
class Boss : Enemy
{
public Boss(string name) : base(name)
{
Console.WriteLine("Boss构造");
}
public override void Attack()
{
Console.WriteLine(Name + "发动攻击");
}
}
Boss boss = new Boss("魔王");
执行顺序还是:
- 先执行父类
Enemy构造函数 - 再执行子类
Boss构造函数
abstract、virtual、override 的关系
abstract 方法:
public abstract void Attack();
特点:
- 没有方法体
- 子类必须实现
- 本身就表示允许被重写,所以子类用
override
virtual 方法:
public virtual void Move()
{
Console.WriteLine("默认移动");
}
特点:
- 有方法体
- 子类可以选择重写,也可以不重写
- 子类重写时也用
override
对比:
abstract:父类不给默认实现,子类必须改virtual:父类给默认实现,子类可以改override:子类真正改写父类的抽象方法或虚方法
抽象方法和普通方法的区别
普通方法:
public void Eat()
{
Console.WriteLine("吃东西");
}
抽象方法:
public abstract void Speak();
区别:
- 普通方法有方法体,抽象方法没有方法体
- 普通方法可以直接继承使用,抽象方法必须由子类实现
- 普通方法可以写在普通类或抽象类里,抽象方法只能写在抽象类里
- 抽象方法天然就是给多态准备的
抽象属性
抽象的不一定只有方法,也可以是属性。
abstract class Weapon
{
public abstract int Damage { get; }
}
class Sword : Weapon
{
public override int Damage
{
get
{
return 10;
}
}
}
简写:
class Bow : Weapon
{
public override int Damage => 8;
}
理解: 父类规定“武器必须有伤害值” 不同武器自己决定伤害值是多少
抽象类和密封类不能同时用
错误写法:
sealed abstract class Animal
{
}
原因:
abstract表示这个类必须被继承后才能补完整sealed表示这个类不能被继承- 两个要求互相矛盾
这个点可以和密封类一起记。
常见错误
错误1:抽象方法写了方法体
abstract class Animal
{
public abstract void Speak()
{
Console.WriteLine("动物叫");
}
}
抽象方法不能有方法体。
错误2:普通类里写抽象方法
class Animal
{
public abstract void Speak();
}
抽象方法只能写在抽象类中。
错误3:子类没实现抽象方法
abstract class Animal
{
public abstract void Speak();
}
class Dog : Animal
{
}
Dog 如果是普通类,就必须 override Speak()。
错误4:抽象方法上再写 virtual
abstract class Animal
{
public abstract virtual void Speak();
}
抽象方法本身就表示必须被重写,不需要再写 virtual。
错误5:直接 new 抽象类
Animal animal = new Animal();
抽象类不能直接实例化。
什么时候用抽象类
适合用抽象类的情况:
- 多个子类确实属于同一种父类
- 父类有公共字段、属性、构造函数或普通方法可以复用
- 父类还需要强制子类实现某些行为
- 你希望调用方通过父类统一调用子类对象
例如:
Animal:动物都能吃,但不同动物叫声不同Shape:图形都有面积,但不同图形面积算法不同Character:角色都有血量,但不同角色攻击方式不同
不适合用抽象类的情况:
- 只是想规定一种能力,例如“能飞”“能攻击”
- 多个类之间没有明确的父子关系
- 一个类需要同时拥有多种能力
这些情况后面通常更适合用接口。
和多态的关系
抽象类经常和多态一起使用。
abstract class Skill
{
public abstract void Release();
}
class FireSkill : Skill
{
public override void Release()
{
Console.WriteLine("释放火球");
}
}
class IceSkill : Skill
{
public override void Release()
{
Console.WriteLine("释放冰锥");
}
}
void UseSkill(Skill skill)
{
skill.Release();
}
UseSkill(new FireSkill());
UseSkill(new IceSkill());
理解:
Skill规定所有技能都必须能Release()FireSkill和IceSkill各自实现释放逻辑UseSkill只依赖抽象父类,不依赖具体技能类
这就是: ==抽象类定规则,多态负责运行时执行不同实现==
注意
- 抽象类不能直接创建对象
- 抽象方法没有方法体,结尾用分号
- 有抽象方法的类必须是抽象类
- 普通子类继承抽象类后,必须实现所有抽象成员
- 抽象类可以有普通方法、字段、属性和构造函数
- 抽象方法不能写
private,因为子类需要能重写它 - 抽象方法不能同时写
virtual,因为它本身就是虚的 - 抽象类不能和
sealed同时使用
一句话记忆
==抽象类不能直接 new,抽象方法只定规则不写实现,子类必须 override。==
再压缩一点: ==父类抽象定规矩,子类重写补细节。==
面试/复习时怎么说
可以这样答:
“抽象类是用 abstract 修饰的类,不能直接实例化,主要用来作为父类。
它可以包含普通成员,也可以包含抽象成员。抽象方法没有方法体,只声明方法签名,普通子类继承后必须用 override 实现。
抽象类适合用来提取多个子类的共同代码,同时强制子类实现不同的行为,常和继承、多态一起使用。”
引用:
- Microsoft Learn: abstract (C# Reference)
- Microsoft Learn: Abstract and Sealed Classes and Class Members
- Microsoft Learn: Inheritance - derive types to create more specialized behavior
#抽象类 #抽象方法 #核心