【CSharp】抽象类和接口详解
抽象类(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
接口。
总结
抽象类和接口的区别
组合使用场景
结合优势:抽象类提供基础实现,接口扩展额外功能。
示例:
Animal
抽象类定义Eat()
和Breathe()
。
IMovable
和IFlyable
接口分别定义移动和飞行能力。
Dog
继承Animal
并实现IMovable
。
Bird
继承Animal
并实现IMovable
和IFlyable
。
何时选择?
抽象类:聚焦于代码复用和层次化设计,适合“是什么”(Is-A)关系。
接口:聚焦于行为契约和功能扩展,适合“能做什么”(Can-Do)关系。
组合使用:通过抽象类提供基础能力,接口扩展多样化功能,实现灵活设计。
- 感谢你赐予我前进的力量