抽象類 (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) | 可以是抽象,也可以有默認實現 |
是否可實例化 | ? 不行 | ? 不行 |
僅供學習參考,