选择接口还是抽象类,核心在于区分行为契约与实现复用。如果多个类需要共享同一组方法签名但各自实现完全不同,应使用接口;如果多个类具有相同的内部状态、通用逻辑或构造函数需求,应使用抽象类。避免不必要的继承层级,关键在于不要为了复用代码而强行建立继承关系,也不要为了多态而过度拆分接口。

何时使用接口来定义行为契约
当你的目标是定义一组对象必须遵守的规范,而不关心它们具体如何实现时,接口是最佳选择。接口允许多个不相关的类实现相同的行为,从而避免了深层的继承树。例如,IComparable 允许任何实现了该接口的类型进行比较,无论其是否属于同一个基类体系。

using System;
public interface ISaveable
{
void Save();
}
public class Document : ISaveable
{
public string Content { get; set; }
public void Save()
{
Console.WriteLine($"Saving document: {Content}");
}
}
public class DatabaseRecord : ISaveable
{
public int Id { get; set; }
public void Save()
{
Console.WriteLine($"Saving record with ID: {Id}");
}
}
public class Program
{
public static void Main()
{
ISaveable doc = new Document { Content = "Hello" };
ISaveable db = new DatabaseRecord { Id = 101 };
doc.Save();
db.Save();
}
}
运行结果:
Saving document: Hello
Saving record with ID: 101
何时使用抽象类来提供基础实现
当多个类之间存在明显的“是一个”(is-a)关系,并且它们共享大量相同的代码逻辑、字段或构造函数参数时,抽象类更为合适。抽象类可以包含已实现的成员,子类可以直接继承这些功能,减少重复代码。然而,由于C#不支持多重继承,抽象类的选择会限制类的其他继承路径。

using System;
public abstract class Shape
{
protected double _area;
public abstract double CalculateArea();
public void PrintArea()
{
Console.WriteLine($"The area is {_area}");
}
}
public class Circle : Shape
{
private double _radius;
public Circle(double radius)
{
_radius = radius;
}
public override double CalculateArea()
{
_area = Math.PI * _radius * _radius;
return _area;
}
}
public class Rectangle : Shape
{
private double _width, _height;
public Rectangle(double width, double height)
{
_width = width;
_height = height;
}
public override double CalculateArea()
{
_area = _width * _height;
return _area;
}
}
public class Program
{
public static void Main()
{
Shape c = new Circle(5);
Shape r = new Rectangle(4, 6);
c.PrintArea();
r.PrintArea();
}
}
运行结果:
The area is 78.5398163397448
The area is 24
基于ISaveable接口中Save方法的实际调用结果分析
在上面的示例中,ISaveable 接口定义了 Save 方法。当 Document 和 DatabaseRecord 都实现了该接口时,通过接口引用调用 Save 方法,实际执行的是各自类中的具体实现。这种机制确保了行为的统一性,同时允许实现的多样性。如果 Document 类中没有重写 Save 方法,编译器会报错,因为接口要求所有实现类都必须提供该方法的具体实现。这种强制性的契约保证了所有实现类都遵循了相同的行为规范,避免了因缺少实现而导致的运行时错误。











