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

抽象类 (abstract class):

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

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

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

虚方法 (virtual):

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

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

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

总结:

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

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

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

🔗 组合使用的优势

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

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

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

    protected abstract void Save(T entity); // 强制子类必须实现
}
cs 复制代码
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

cs 复制代码
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>

cs 复制代码
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

cs 复制代码
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

cs 复制代码
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);
    }
}

🧩 步骤四:在服务中使用

cs 复制代码
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) 可以是抽象,也可以有默认实现
是否可实例化 ❌ 不行 ❌ 不行

++仅供学习参考,++

相关推荐
bryant_meng1 个月前
【C++】Virtual function and Polymorphism
c++·多态·抽象类·虚函数·纯虚函数
蟹至之2 个月前
类和对象(5)——抽象类和接口
java·接口·类和对象·抽象类
lihan_freak3 个月前
java中的抽象类和接口
java·开发语言·接口·抽象类
慧都小妮子4 个月前
借助Dynamsoft的批量条码扫描,推动无人机仓储管理新高度
无人机·库存管理·条码扫描·仓储
陈建1111 年前
设计模式学习笔记 - 面向对象 - 5.接口和抽象类的区别
接口·抽象类
牛马程序员‍1 年前
学习JavaEE的日子 day16 抽象类,接口,多态,对象转型,内部类
java-ee·接口·多态·抽象类·对象转型·内部类
bit_Sakura1 年前
面向对象编程(二)多态、接口
java·接口·多态·抽象类
.29.1 年前
【Java】接口和抽象类有什么共同点和区别?
java·开发语言·python·接口·abstract·抽象类·interface
xiangpingeasy1 年前
PHP中什么是抽象类?与接口有何区别?
开发语言·php·接口·抽象类