抽象类与接口的区别?什么时候使用抽象类,什么时候使用接口?


一、核心区别对比

特性 抽象类 (Abstract Class) 接口 (Interface)
关键字 abstract class interface
继承方式 单继承(一个类只能继承一个抽象类) 多实现(一个类可以实现多个接口)
方法实现 可包含具体方法、抽象方法、虚方法等 C# 8.0之前仅支持方法签名;之后支持默认实现
字段与属性 支持实例字段、自动属性、具实现的属性 仅支持自动属性(C# 8.0后支持默认实现)
访问修饰符 可使用 public, protected, private 成员默认为 public,不能显式指定
构造函数 可定义构造函数 不可定义构造函数
设计意图 提供一个基类模板,包含部分通用实现 定义一组契约,规定类必须提供的功能
密封性 不能被实例化 不能被实例化
静态成员 不支持 C# 8.0后支持静态成员

二、使用场景分析

1. 使用抽象类的场景

  • 代码复用与状态共享:当多个类需要共享代码和状态(如公共字段、受保护的方法)时。例如,所有形状都有颜色,可以将其定义在抽象基类中。
  • 强关联的类型体系:当类之间存在明确的“是...一种”(is-a)关系时。例如,Animal 是一个抽象类,DogCat 都是它的子类。
  • 控制初始化过程:通过构造函数确保子类在创建时进行必要的初始化设置。
  • 提供默认实现但允许重写:定义一些虚方法 (virtual),让子类可以选择性地重写。
  • 示例
    public abstract class Animal
    {
        protected string Name { get; set; }
        protected int Age { get; set; }
    
        // 构造函数,用于初始化
        protected Animal(string name, int age)
        {
            Name = name;
            Age = age;
        }
    
        // 具体方法,所有子类共享
        public void Sleep()
        {
            Console.WriteLine($"{Name} is sleeping.");
        }
    
        // 抽象方法,强制子类实现
        public abstract void MakeSound();
    
        // 虚方法,子类可选择重写
        public virtual void Move()
        {
            Console.WriteLine($"{Name} is moving.");
        }
    }
    
    public class Dog : Animal
    {
        public Dog(string name, int age) : base(name, age) { }
    
        public override void MakeSound()
        {
            Console.WriteLine("Woof!");
        }
    }
    

2. 使用接口的场景

  • 定义行为契约:当需要定义对象能够执行什么操作,而不关心它是什么类型时。例如,任何能“飞行”的对象都可以实现 IFlyable 接口。
  • 实现多重继承效果:一个类可以实现多个接口,从而获得多种能力。
  • 解耦与依赖注入:接口使代码更灵活,易于单元测试和模块替换。依赖于接口而不是具体实现是面向接口编程的核心。
  • 无关类型间建立联系:让完全不同的类实现相同的接口,以表示它们具有某种共同的能力。例如,BirdAirplane 都可以实现 IFlyable
  • 示例
    public interface IFlyable
    {
        void Fly();
    }
    
    public interface IFeedable
    {
        void Eat();
    }
    
    public class Bird : IFlyable, IFeedable // 实现多个接口
    {
        public void Fly() => Console.WriteLine("Bird is flying.");
        public void Eat() => Console.WriteLine("Bird is eating.");
    }
    
    public class Airplane : IFlyable
    {
        public void Fly() => Console.WriteLine("Airplane is flying.");
    }
    

三、选择建议总结

  • 选抽象类:当你有一组紧密相关的类,并且希望它们共享代码、数据(字段)或者有一个共同的基类时。
  • 选接口:当你想定义一个对象的能力,或者一个类需要从多个来源继承功能,或者为了提高代码的灵活性和可测试性时。

核心原则:抽象类侧重于“是什么”,强调类的共性和复用;接口侧重于“能做什么”,强调行为的规范和能力的组合。在现代C#(特别是8.0以后)中,虽然接口的功能变得更加强大,但其设计初衷未变,选择时仍应基于“是什么”还是“能做什么”的逻辑来判断。

posted @ 2026-02-03 17:12  蓝天下e_e  阅读(12)  评论(0)    收藏  举报