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

抽象类 (abstract class):

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

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

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

虚方法 (virtual):

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

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

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

总结:

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

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

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

🔗 组合使用的优势

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

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

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

    protected abstract void Save(T entity); // 强制子类必须实现
}
public class UserRepository : BaseRepository<User>
{
    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<T>

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

🧱 步骤二:实现抽象类 BaseRepository<T>

以 EF Core 为例,注入 DbContext

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

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

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

    public virtual async Task<IEnumerable<T>> 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<User>
{
    public UserRepository(MyDbContext context) : base(context)
    {
    }

    // 可扩展自定义方法
    public async Task<User?> 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)可以是抽象,也可以有默认实现
是否可实例化❌ 不行❌ 不行

仅供学习参考,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值