C#抽象类和虚方法的作用是什么?

抽象类 (abstract class):

  • 不能直接实例化,只能被继承。

  • 用来定义一套基础框架和规范,强制子类必须实现某些方法(抽象方法)。

  • 可用来封装一些共通的逻辑,减少代码重复。

虚方法 (virtual):

  • 表示这个方法可以被子类重写(override)

  • 默认给了一套实现,你可以用,也可以替换掉

  • 避免了子类必须每次都写重复代码(子类用基类实现就好)

总结:

抽象类 + 虚方法组合使用的好处是:

  • 提供一个统一的接口和逻辑框架

  • 允许子类在不破坏主结构的情况下实现个性化逻辑(比如加缓存、记录日志)

组合使用的优势

抽象类 + 虚方法
✅ 定义统一规范和基础结构
✅ 提供默认逻辑(虚方法)
✅ 允许子类按需定制(重写虚方法)
✅ 提高代码复用性、可维护性
✅ 非侵入式扩展逻辑(如:记录日志、缓存等)

  举个实际应用场景(例如仓储):

public abstract class BaseRepository
{
    public virtual void Add(T entity)
    {
        // 默认实现:记录日志 + 保存
        Console.WriteLine("添加前记录日志");
        Save(entity);
    }

    protected abstract void Save(T entity); // 强制子类必须实现
}
public class UserRepository : BaseRepository
{
    protected override void Save(User entity)
    {
        // 实现具体的保存逻辑
        Console.WriteLine("保存用户到数据库");
    }

    public override void Add(User entity)
    {
        // 也可以选择重写 Add,增加缓存逻辑等
        base.Add(entity);
        Console.WriteLine("添加用户成功");
    }
}

✅ 示例代码:调用 UserRepository

public class Program
{
    public static void Main(string[] args)
    {
        var userRepo = new UserRepository();

        var newUser = new User { Id = 1, Name = "张三" };
        userRepo.Add(newUser);

        /* 日志打印结果
        添加前记录日志
        保存用户到数据库
        添加用户成功
        */
    }
}

// 假设 User 类如下:
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}



实战目标

构建一个 基于接口 + 抽象类 + 泛型 的通用仓储:

  • 支持常规操作(增删改查)

  • 支持扩展方法(如分页、条件查询)

  • 易于继承 & 复用


步骤一:定义接口 IRepository

public interface IRepository where T : class
{
    Task GetByIdAsync(int id);
    Task> GetAllAsync();
    Task AddAsync(T entity);
    void Update(T entity);
    void Delete(T entity);
}

步骤二:实现抽象类 BaseRepository

以 EF Core 为例,注入 DbContext

public abstract class BaseRepository : IRepository where T : class
{
    protected readonly DbContext _context;
    protected readonly DbSet _dbSet;

    public BaseRepository(DbContext context)
    {
        _context = context;
        _dbSet = _context.Set();
    }

    public virtual async Task GetByIdAsync(int id)
    {
        return await _dbSet.FindAsync(id);
    }

    public virtual async Task> GetAllAsync()
    {
        return await _dbSet.ToListAsync();
    }

    public virtual async Task AddAsync(T entity)
    {
        await _dbSet.AddAsync(entity);
    }

    public virtual void Update(T entity)
    {
        _dbSet.Update(entity);
    }

    public virtual void Delete(T entity)
    {
        _dbSet.Remove(entity);
    }
}

步骤三:创建具体仓储类 UserRepository

public class UserRepository : BaseRepository
{
    public UserRepository(MyDbContext context) : base(context)
    {
    }

    // 可扩展自定义方法
    public async Task GetByEmailAsync(string email)
    {
        return await _dbSet.FirstOrDefaultAsync(u => u.Email == email);
    }
}

步骤四:在服务中使用

public class UserService
{
    private readonly UserRepository _userRepo;

    public UserService(UserRepository userRepo)
    {
        _userRepo = userRepo;
    }

    public async Task RegisterUser(User user)
    {
        await _userRepo.AddAsync(user);
        // 保存到数据库由 UnitOfWork 或 DbContext 控制
    }
}

✅ 什么时候用接口 vs 抽象类?

特性 接口(interface) 抽象类(abstract class)
目的 定义行为规范 定义基本结构和部分实现
支持多继承 ✅ 支持 ❌ 不支持
可包含字段 ❌ 不行 ✅ 可以
可有构造函数 ❌ 不行 ✅ 可以
成员默认类型 抽象(abstract) 可以是抽象,也可以有默认实现
是否可实例化 ❌ 不行 ❌ 不行

仅供学习参考,

你可能感兴趣的:(抽象类,虚方法,仓储)