在C#项目里,代码可重用意味着同一段逻辑可以在不同模块、不同项目甚至不同团队之间被稳定调用,而不需要反复复制修改。实现可重用并不只是把方法改成public,而是从设计层面降低耦合、明确边界,并让变化点可控。

一、遵循单一职责与抽象依赖
要让一段C#代码被多处复用,第一步是保证它只做一件事。如果一个方法既查数据库又处理界面提示,那它几乎不可能在别的控制台程序里直接用。把功能拆小之后,再通过接口而非具体类来暴露能力,调用方就不会被某个实现绑死。
下面例子中,IRepository 作为抽象,让业务层不依赖具体的SqlServer实现,后续换成MySQL或内存仓储都不影响上层:
public interface IRepository<T> where T : class
{
T GetById(int id);
void Add(T entity);
}
public class UserServie
{
private readonly IRepository<User> _repo;
// 依赖抽象,不关心底层是哪种数据库
public UserServie(IRepository<User> repo)
{
_repo = repo;
}
public User Find(int id)
{
return _repo.GetById(id);
}
}
这种写法在单元测试时也更容易伪造接口,用内存数据代替真实数据库,进一步提升代码可测试性与可重用性。
二、使用泛型消除类型重复
很多数据操作对不同类型的实体逻辑完全一致,只是类名不同。如果为每个实体都写一套增删改查,不仅枯燥还容易遗漏修改。C#的泛型可以在编译期保留类型安全,又避免写多份相似代码。
例如一个通用的分页方法,不管是订单还是用户都能复用:
public class Pager
{
// 泛型方法,适用于任何IQueryable数据源
public static List<T> Page<T>(IQueryable<T> source, int pageIndex, int pageSize)
{
return source
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.ToList();
}
}
// 调用时类型由编译器推断
var users = Pager.Page(userQuery, 1, 20);
var orders = Pager.Page(orderQuery, 2, 15);
泛型配合约束(where子句)还能限制可用类型,避免运行时才暴露错误。当逻辑确实与类型无关时,泛型是降低重复代码最直接的方式。
三、借助扩展方法增强已有类型
有时我们需要给框架自带的类型(如string、DateTime)增加小工具方法,但又不能修改其源码。C#的扩展方法允许在不继承、不包装的前提下追加静态行为,非常适合放置跨项目公用的辅助逻辑。
比如统一处理字符串为空的情况:
public static class StringExtensions
{
// this关键字标记扩展方法
public static bool IsNullOrEmpty(this string value)
{
return string.IsNullOrEmpty(value);
}
public static string SafeTrim(this string value)
{
return value == null ? string.Empty : value.Trim();
}
}
// 使用起来如同实例方法
string name = null;
if (name.IsNullOrEmpty()) { /* 复用逻辑 */ }
把这些扩展放在独立类库里,多个项目引用后即可共享,且不会污染原有类型的继承体系。需要注意的是,扩展方法优先级低于类型自身的实例方法,因此命名时应避免和将来框架新增方法冲突。
四、用设计模式隔离变化点
当一段代码在多个场景出现,但细节略有差异时,可用策略模式或模板方法把稳定部分和易变部分拆开。这样主干逻辑写成可重用基类,差异点由子类或委托注入。
以报表导出为例,导出流程固定,但格式不同:
public abstract class ReportExporter
{
// 模板方法,定义稳定流程
public void Export(List<Data> data)
{
var content = BuildContent(data);
WriteFile(content);
}
protected abstract string BuildContent(List<Data> data);
private void WriteFile(string content)
{
// 共用写文件逻辑
File.WriteAllText("report.txt", content);
}
}
public class CsvExporter : ReportExporter
{
protected override string BuildContent(List<Data> data)
{
// 仅CSV格式差异
return string.Join("n", data.Select(d => d.Name + "," + d.Value));
}
}
通过这种模式,新增Excel导出只需再加一个子类,原有导出流程完全不动。对于调用方来说,依赖的始终是ReportExporter抽象,符合开放封闭原则,也显著提高了代码复用率。
五、避免过度设计与复用边界
并不是所有重复都值得抽象。如果某段代码只会在两个地方出现,且业务含义不同,强行提取反而让系统更难理解。经验上,当相似逻辑第三次出现时再抽组件更为稳妥。
另外,可重用组件应减少对外界配置和静态状态的依赖。一个好用的工具类应当像纯函数那样,输入确定则输出确定,不悄悄读取全局变量。这样才能在任意新项目中安全引用,而不会引发难以排查的副作用。
综上,C#代码可重用是一套组合拳:小方法、抽象接口、泛型、扩展方法和适度模式共同起作用。从真实业务痛点出发,逐步沉淀稳定类库,才能让团队在后续开发中真正少写代码、多交付功能。