抽象类(Abstract Class)

概念

  • 不能实例化:抽象类不能被实例化,它通常作为基类存在,为子类提供一套通用的接口和部分实现。

  • 包含实现:抽象类可以包含具体的方法实现和抽象方法。抽象方法必须在子类中被重写。

  • 单继承:一个类只能继承自一个抽象类(C#中不支持多重继承)。

应用场景

当你希望提供一个通用的基类,该基类定义了一些子类共有的方法实现,并且还有一些方法需要由子类提供具体实现时,使用抽象类是一个不错的选择。

示例

示例1:图形基类

namespace App01
{
    // 抽象的图形基类
    public abstract class Shape
    {
        // 抽象方法:计算面积
        public abstract double Area();

        // 具体实现的方法:显示形状信息
        public void Display()
        {
            Console.WriteLine("This is a shape.");
        }
    }

    // 圆形类,继承自Shape
    publicclass Circle : Shape
    {
        publicdouble Radius { get; set; }

        // 重写抽象方法:计算圆的面积
        public override double Area()
        {
            return Math.PI * Radius * Radius;
        }
    }

    // 矩形类,继承自Shape
    publicclass Rectangle : Shape
    {
        publicdouble Width { get; set; }
        publicdouble Height { get; set; }

        // 重写抽象方法:计算矩形的面积
        public override double Area()
        {
            return Width * Height;
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            Circle circle = new Circle();
            circle.Radius = 5;
            Console.WriteLine("The area of the circle is {0}.", circle.Area());

            Rectangle rectangle = new Rectangle();
            rectangle.Width = 4;
            rectangle.Height = 6;
            Console.WriteLine("The area of the rectangle is {0}.", rectangle.Area());
        }
    }
}

说明:

  • Shape​类是一个抽象类,包含一个抽象方法Area()​和一个具体方法Display()​

  • Circle​Rectangle​类继承自Shape​,并实现了Area()​方法。

示例2:动物基类

namespace App02  
{
    // 抽象的动物基类
    public abstract class Animal
    {
        // 抽象方法:发出叫声
        public abstract void MakeSound();

        // 具体方法:共同的行为
        public void Sleep()
        {
            Console.WriteLine("The animal is sleeping.");
        }
    }

    // 狗类,继承自Animal
    publicclass Dog : Animal
    {
        public override void MakeSound()
        {
            Console.WriteLine("Dog barks: Woof!");
        }
    }

    // 猫类,继承自Animal
    publicclass Cat : Animal
    {
        public override void MakeSound()
        {
            Console.WriteLine("Cat meows: Meow!");
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            // 创建Dog对象
            Dog dog = new Dog();
            // 调用Dog的共同行为
            dog.Sleep();
            // 调用Dog的叫声
            dog.MakeSound();
            // 创建Cat对象
            Cat cat = new Cat();
            // 调用Cat的共同行为
            cat.Sleep();
            // 调用Cat的叫声
            cat.MakeSound();
        }
    }
}

说明:

  • Animal​类定义了共有的行为Sleep()​,并要求子类实现MakeSound()​方法。

接口(Interface)

概念

  • 完全抽象:接口只能包含方法、属性、事件、索引器的声明,不能包含任何实现。

  • 多实现:一个类可以实现多个接口,实现接口即需要实现其所有成员。

  • 成员默认是公共的:接口成员默认是公共的,不能包含访问修饰符。

应用场景

当你希望定义一组不相关类之间的通用行为契约,并且不涉及实现细节时,接口是最好的选择。

示例

示例1:可绘制和可变形

namespace App01
{
    // 可绘制的接口
    public interface IDrawable
    {
        void Draw();
    }

    // 可变形的接口
    public interface ITransformable
    {
        void Rotate(double angle);
        void Scale(double factor);
    }

    // 实现了IDrawable和ITransformable的形状类
    publicclass TransformableShape : IDrawable, ITransformable
    {
        public void Draw()
        {
            Console.WriteLine("Drawing the shape.");
        }

        public void Rotate(double angle)
        {
            Console.WriteLine($"Rotating the shape by {angle} degrees.");
        }

        public void Scale(double factor)
        {
            Console.WriteLine($"Scaling the shape by a factor of {factor}.");
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            TransformableShape shape = new TransformableShape();
            shape.Draw();
            shape.Rotate(45);
            shape.Scale(2.0);
            Console.ReadKey();
        }
    }
}

说明:

  • ​IDrawable​ITransformable​是两个接口,定义了绘制和变形的行为。

  • TransformableShape​类实现了这两个接口,必须提供所有方法的实现。

示例2:数据存储接口

namespace App02
{
    // 数据存储接口
    public interface IDataStore
    {
        void Save(string data);
        string Load();
    }

    // 本地文件存储类
    publicclass FileDataStore : IDataStore
    {
        public void Save(string data)
        {
            Console.WriteLine("Saving data to file.");
            // 实际的文件保存逻辑
        }

        public string Load()
        {
            Console.WriteLine("Loading data from file.");
            // 实际的文件加载逻辑
            return"Data from file";
        }
    }

    // 云端存储类
    publicclass CloudDataStore : IDataStore
    {
        public void Save(string data)
        {
            Console.WriteLine("Saving data to cloud.");
            // 实际的云保存逻辑
        }

        public string Load()
        {
            Console.WriteLine("Loading data from cloud.");
            // 实际的云加载逻辑
            return"Data from cloud";
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            // 选择存储方式
            IDataStore dataStore = new FileDataStore();
            // 保存数据
            dataStore.Save("Some data");
            // 加载数据
            string loadedData = dataStore.Load();
            Console.WriteLine("Loaded data: " + loadedData);
        }
    }
}

抽象类与接口的组合使用

有时,我们可以将抽象类和接口结合起来使用,以充分利用它们的优势。

示例:动物行为

namespace App03
{
    // 抽象的动物类
    public abstract class Animal
    {
        public abstract void Eat();

        public void Breathe()
        {
            Console.WriteLine("Animal breathes.");
        }
    }

    // 可移动的接口
    public interface IMovable
    {
        void Move();
    }

    // 可飞行的接口
    public interface IFlyable
    {
        void Fly();
    }

    // 狗类,继承自Animal并实现IMovable接口
    publicclass Dog : Animal, IMovable
    {
        public override void Eat()
        {
            Console.WriteLine("Dog eats.");
        }

        public void Move()
        {
            Console.WriteLine("Dog runs.");
        }
    }

    // 鸟类,继承自Animal并实现IMovable和IFlyable接口
    publicclass Bird : Animal, IMovable, IFlyable
    {
        public override void Eat()
        {
            Console.WriteLine("Bird eats.");
        }

        public void Move()
        {
            Console.WriteLine("Bird walks.");
        }

        public void Fly()
        {
            Console.WriteLine("Bird flies.");
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            // 创建Dog对象并调用方法
            Dog dog = new Dog();
            dog.Eat();
            dog.Breathe();
            dog.Move();
            // 创建Bird对象并调用方法
            Bird bird = new Bird();
            bird.Eat();
            bird.Breathe();
            bird.Move();
            bird.Fly();
            Console.ReadKey();
        }
    }
}

说明:

  • Animal​是一个抽象类,定义了所有动物的共有行为。

  • IMovable​和IFlyable​是接口,定义了可移动和可飞行的行为。

  • Dog​类继承自Animal​并实现了IMovable​接口。

  • Bird​类继承自Animal​并实现了IMovable​和IFlyable​接口。

总结

抽象类和接口的区别

特性

抽象类

接口

实例化

❌ 不能实例化

❌ 不能实例化

实现内容

✅ 可包含具体方法和抽象方法

❌ 仅声明成员,无实现

继承/实现数量

单继承(一个子类只能继承一个父类)

多实现(一个类可实现多个接口)

成员访问修饰符

支持(如public​、protected​)

成员默认public​,不可显式修饰

字段/属性

✅ 可定义字段、属性

❌ 只能声明属性(无字段)

设计目的

提供代码复用和部分通用逻辑

定义行为契约,实现多态

组合使用场景

  • 结合优势:抽象类提供基础实现,接口扩展额外功能

    • 示例

      • Animal​抽象类定义Eat()​Breathe()​。

      • IMovable​和IFlyable​接口分别定义移动和飞行能力。

      • Dog​继承Animal​并实现IMovable​。

      • Bird​继承Animal​并实现IMovable​和IFlyable​。

何时选择?

  • 抽象类:聚焦于代码复用层次化设计,适合“是什么”(Is-A)关系。

  • 接口:聚焦于行为契约功能扩展,适合“能做什么”(Can-Do)关系。

  • 组合使用:通过抽象类提供基础能力,接口扩展多样化功能,实现灵活设计。