抽象类是 C# 面向对象编程中的一个重要概念,它介于普通类和接口之间,提供了一种定义部分实现并要求派生类完成其余部分的机制。
一、C# 中的抽象类
抽象类是 C# 面向对象编程中的一个重要概念,它介于普通类和接口之间,提供了一种定义部分实现并要求派生类完成其余部分的机制。
1. 抽象类的基本概念
抽象类是不能被实例化的类,主要用于作为其他类的基类。它通过 abstract
关键字声明:
public abstract class Animal
{// 类成员
}
2. 抽象类的特点
- 不能被实例化:不能创建抽象类的对象
- 可以包含抽象成员:这些成员必须在派生类中实现
- 可以包含具体实现:与接口不同,抽象类可以包含已实现的方法
- 可以包含字段、属性、方法、事件等:功能比接口更全面
- 支持访问修饰符:可以控制成员的访问级别
3. 抽象成员
抽象类可以包含抽象成员(方法、属性、索引器或事件):
public abstract class Animal
{// 抽象方法(无实现)public abstract void MakeSound();// 抽象属性public abstract string Name { get; set; }
}
派生类必须实现所有抽象成员:
public class Dog : Animal
{public override void MakeSound(){Console.WriteLine("Woof!");}private string _name;public override string Name {get { return _name; }set { _name = value; }}
}
4. 抽象类与具体类的比较
特性 | 抽象类 | 具体类 |
---|---|---|
实例化 | 不能 | 能 |
包含抽象成员 | 可以 | 不能 |
包含具体实现 | 可以 | 可以 |
继承要求 | 必须实现所有抽象成员 | 无特殊要求 |
5. 抽象类与接口的比较
特性 | 抽象类 | 接口 |
---|---|---|
实现 | 部分实现 | 无实现 |
多继承 | 不支持 | 支持 |
字段 | 可以包含 | 不能包含 |
构造函数 | 可以有 | 不能有 |
访问修饰符 | 支持 | 默认public |
版本控制 | 添加新成员可能破坏派生类 | 添加新成员会破坏实现 |
6. 何时使用抽象类
- 当多个相关类共享通用功能,但各自又有独特行为时
- 需要提供一些基础实现,同时要求派生类实现特定功能时
- 需要包含非公共成员(protected/internal)时
- 需要定义构造函数、析构函数或静态成员时
7. 抽象类的实际示例
public abstract class Shape
{// 抽象方法 - 必须被派生类实现public abstract double Area();// 具体方法 - 已实现public void Display(){Console.WriteLine($"Area: {Area()}");}// 抽象属性public abstract string Color { get; set; }
}public class Circle : Shape
{public double Radius { get; set; }public override string Color { get; set; }public override double Area(){return Math.PI * Radius * Radius;}
}public class Rectangle : Shape
{public double Width { get; set; }public double Height { get; set; }public override string Color { get; set; }public override double Area(){return Width * Height;}
}
8. 抽象类的进阶用法
8.1 抽象类中的构造函数
public abstract class Vehicle
{protected string _model;protected Vehicle(string model){_model = model;}public abstract void Drive();
}public class Car : Vehicle
{public Car(string model) : base(model) { }public override void Drive(){Console.WriteLine($"Driving {_model}");}
}
8.2 部分实现的模板方法模式
public abstract class DataProcessor
{// 模板方法public void Process(){ReadData();ProcessData();SaveResult();}protected abstract void ReadData();protected abstract void ProcessData();protected virtual void SaveResult(){Console.WriteLine("Default save implementation");}
}
9. 总结
抽象类是 C# 中实现多态和代码重用的强大工具,它:
- 强制派生类实现特定行为
- 提供共享的基类实现
- 比接口更灵活,可以包含状态和行为
- 适合构建具有层次结构的类家族
正确使用抽象类可以使代码更加清晰、可维护,并减少重复代码。
二、C# 中的接口
接口是 C# 中实现多态和抽象的核心机制之一,它定义了一组相关功能的契约,而不提供具体实现。
1. 接口的基本概念
接口使用 interface
关键字声明:
public interface IAnimal
{void MakeSound();string Name { get; set; }
}
2. 接口的特点
- 纯契约:只包含成员声明,不包含实现
- 多继承:一个类可以实现多个接口
- 隐式公开:所有成员默认是 public 的
- 不能包含字段:只能包含方法、属性、事件和索引器
- 不能有构造函数:因为不能被实例化
- 不能包含静态成员(C# 8.0 前)
3. 接口成员的实现
类通过 :
符号实现接口,并提供具体实现:
public class Dog : IAnimal
{private string _name;public void MakeSound(){Console.WriteLine("Woof!");}public string Name {get { return _name; }set { _name = value; }}
}
4. 显式接口实现
当多个接口有同名成员时,可使用显式实现:
public class Animal : IAnimal, IDisposable
{void IAnimal.MakeSound(){Console.WriteLine("Animal sound");}void IDisposable.Dispose(){Console.WriteLine("Disposing");}
}
5. 接口与抽象类的比较
特性 | 接口 | 抽象类 |
---|---|---|
实现 | 无 | 可以有 |
多继承 | 支持 | 不支持 |
字段 | 不能有 | 可以有 |
构造函数 | 不能有 | 可以有 |
版本控制 | 添加成员会破坏实现 | 更灵活 |
访问修饰符 | 默认public | 可控制 |
6. 接口的默认实现(C# 8.0+)
C# 8.0 引入了接口默认实现:
public interface ILogger
{void Log(string message);// 默认实现void LogError(string error){Log($"Error: {error}");}
}
7. 接口的实际应用示例
7.1 依赖注入
public interface IRepository
{void Save(Customer customer);Customer GetById(int id);
}public class SqlRepository : IRepository
{public void Save(Customer customer) { /* SQL实现 */ }public Customer GetById(int id) { /* SQL实现 */ }
}public class CustomerService
{private readonly IRepository _repository;public CustomerService(IRepository repository){_repository = repository;}public void AddCustomer(Customer customer){_repository.Save(customer);}
}
7.2 策略模式
public interface ISortStrategy
{void Sort(List<int> list);
}public class QuickSort : ISortStrategy
{public void Sort(List<int> list) { /* 快速排序实现 */ }
}public class BubbleSort : ISortStrategy
{public void Sort(List<int> list) { /* 冒泡排序实现 */ }
}public class Sorter
{private ISortStrategy _strategy;public Sorter(ISortStrategy strategy){_strategy = strategy;}public void Sort(List<int> list){_strategy.Sort(list);}
}
8. 接口的高级特性
8.1 接口继承
接口可以继承其他接口:
public interface IReadableRepository
{object GetById(int id);
}public interface IWritableRepository : IReadableRepository
{void Save(object entity);
}
8.2 泛型接口
public interface IRepository<T>
{void Add(T entity);T GetById(int id);IEnumerable<T> GetAll();
}
8.3 协变和逆变接口(C# 4.0+)
// 协变(out)
public interface IEnumerable<out T> { /*...*/ }// 逆变(in)
public interface IComparer<in T> { /*...*/ }
9. 接口的使用场景
- 定义跨类别的行为:如
IDisposable
- 实现多继承:一个类可以实现多个接口
- 依赖注入:通过接口解耦依赖
- 单元测试:通过接口创建Mock对象
- 插件架构:定义扩展点
- 策略模式:定义算法族
10. 接口设计的最佳实践
- 单一职责:每个接口应专注于一个特定功能
- 命名规范:以"I"开头,如
IEnumerable
- 避免过大接口:防止实现类负担过重
- 优先组合而非继承:通过多个小接口组合功能
- 考虑向后兼容:添加新成员可能破坏现有实现
11. 总结
C# 中的接口是:
- 定义行为契约的强大工具
- 实现松耦合设计的关键
- 支持多继承的机制
- 现代C#中功能不断增强(默认实现等)
正确使用接口可以使代码更加灵活、可测试和可维护,是面向对象设计和架构设计中的重要组成部分。