抽象类必须用 abstract
修饰,且不能被实例化
定义抽象类时,
class前必须加
abstract关键字,否则编译器会报错
CS0518: Predefined type 'System.Object' is not defined or imported或更直接的
CS0144: Cannot create an instance of the abstract class or abstract method。哪怕类里一个抽象成员都没有,只要标了
abstract,就不能写
new MyAbstractClass()。
常见误操作:
忘记加abstract却写了
abstract void DoSomething();→ 编译失败,提示“包含抽象方法的类必须声明为
abstract” 加了
abstract却试图
new它 → 运行前就报错,IDE 通常红色波浪线下划线 把
abstract错写成
virtual或
sealed→ 语义完全错误,行为不可控
abstract
方法不能有方法体,子类必须用 override
实现
abstract方法只声明签名,不提供实现,连大括号
{} 都不能有。子类继承后,必须用 public override(或符合访问级别的
override)给出具体逻辑,否则子类也得标
abstract。
abstract class Animal
{
public abstract void MakeSound(); // ✅ 正确:无实现
// public abstract void Sleep() { } // ❌ 编译错误:abstract 方法不能有主体
}
<p>class Dog : Animal
{
public override void MakeSound() // ✅ 必须 override,且访问修饰符不能比基类更严格
{
Console.WriteLine("Woof!");
}
}注意点:
abstract方法默认是
public,但显式写
public abstract更清晰;不能用
private abstract(编译器直接拒绝) 子类若没实现全部
abstract成员,自身必须也声明为
abstract
abstract方法不能是
static、
virtual或
sealed
抽象类可以含普通成员、virtual
方法和构造函数
抽象类不是“空架子”,它可以像普通类一样拥有字段、属性、非抽象方法、
virtual方法,甚至带参数的构造函数——这些都会被子类继承并可直接调用。
abstract class Shape
{
protected string name;
<pre class='brush:php;toolbar:false;'>protected Shape(string name) => this.name = name; // ✅ 抽象类可以有构造函数
public abstract double GetArea(); // 子类必须实现
public virtual void Describe() => Console.WriteLine($"This is a {name}"); // ✅ 可被重写,也可直接调用
public void PrintType() => Console.WriteLine("Shape"); // ✅ 普通方法,子类自动继承}
class Circle : Shape { private readonly double radius; public Circle(double r) : base("Circle") // ✅ 必须调用基类构造函数 { radius = r; }
public override double GetArea() => Math.PI * radius * radius;
}
关键细节:
子类构造函数必须通过: base(...)调用抽象基类的构造函数(如果有)
virtual方法在抽象类中很常用:提供默认行为,允许子类选择性重写 字段和
protected属性是子类共享状态的常用方式,但要注意封装边界
抽象类 vs 接口:选谁取决于“有没有共同状态或默认实现”
如果多个类型需要共享字段、构造逻辑、部分实现代码,或者你希望强制子类走某个初始化流程(比如必须传参进构造函数),就用抽象类。接口只描述“能做什么”,不提供状态或实现。
典型判断信号:
需要protected字段或
internal辅助方法?→ 抽象类 想让子类自动获得一个
LogCreated()默认日志行为?→ 抽象类 +
virtual方法 只是约定一组行为(如
IDisposable、
IComparable<t></t>),且不同实现完全无关?→ 接口 一个类要同时满足多种契约(如既可比较又可序列化)?→ 必须用接口,因为 C# 不支持多继承
别为了“听起来高级”而硬套抽象类。很多初学者把所有基类都设成
abstract,结果发现子类全得写重复构造逻辑,反而增加维护成本。
