在c#中,接口和抽象类都能隐藏具体实现,但核心区别在于约束的维度:接口定义“能做什么”(能力契约),不包含任何状态或实现细节;抽象类定义“是什么”(身份与共性),可以包含部分实现、字段和构造函数。选择时,若需支持多继承或共享代码逻辑,用抽象类;若需灵活组合行为或解耦依赖,用接口。

接口如何强制实现者提供特定方法
接口通过声明一组方法签名而不提供实现,强制所有实现类必须提供这些方法的具体代码。这种机制确保了调用方可以通过统一的接口类型访问不同类的实例,而无需关心底层是哪种具体对象。例如,定义一个 IAnimal 接口,要求实现类必须包含 Eat() 方法,这样任何实现了该接口的类都必须重写此方法,从而隐藏了具体的进食逻辑。

public interface IAnimal
{
void Eat();
}
public class Dog : IAnimal
{
public void Eat()
{
Console.WriteLine("Dog is eating bones.");
}
}
public class Cat : IAnimal
{
public void Eat()
{
Console.WriteLine("Cat is eating fish.");
}
}
抽象类如何实现代码复用并限制子类范围
抽象类允许定义一些通用的属性和方法实现,子类继承时必须使用这些共享逻辑,同时可以扩展自己的特有行为。这种方式适合具有明确层级关系的类族,避免重复编写相同的初始化或辅助代码。例如,创建一个 Shape 抽象类,其中包含计算面积的通用框架,但将具体计算留给子类实现,既保证了结构一致,又隐藏了内部计算差异。

public abstract class Shape
{
protected string Name;
public Shape(string name)
{
Name = name;
}
public void Describe()
{
Console.WriteLine($"I am a {Name}.");
}
public abstract double CalculateArea();
}
public class Circle : Shape
{
private double Radius;
public Circle(double radius) : base("Circle")
{
Radius = radius;
}
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
}
当基类需要初始化资源时接口无法直接处理
如果某个功能模块需要在创建实例时进行复杂的资源分配或状态检查,抽象类可以通过构造函数完成这一过程,而接口由于不能包含构造函数,无法直接承担此责任。此时,若强行使用接口,必须在外部工厂或构造函数中额外处理初始化逻辑,导致耦合度增加。例如,在 DatabaseConnection 抽象类中,可以在构造函数中建立连接池,确保子类直接使用已初始化的环境,而接口仅负责暴露操作方法。


















