找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

618

积分

0

好友

80

主题
发表于 15 小时前 | 查看: 0| 回复: 0

在面向对象编程(OOP)中,四大核心概念——抽象、封装、继承和多态——指导着我们如何组织代码和复用功能。除了语言特性,OOP更是一种设计理念:通过对象组合来构建灵活、可维护的系统。

然而,在实际开发中,直接依赖继承来复用功能往往会带来较高的耦合度和维护成本。GoF 设计模式提出的 “组合优于继承(Composition over Inheritance)” 原则指出,通过组合已有对象而非继承基类来复用功能,通常更灵活、更安全,也更易于扩展。

组合与继承关系对比示意图

C# 开发中,这一思想同样适用。TypeAdoption 库正是基于组合优先的理念,为开发者提供了一种简洁、高效的方式实现 接口委托组合模式。借助 Roslyn 源生成器,TypeAdoption 在编译阶段自动生成委托代码,避免重复样板,同时保证类型安全,使功能复用既灵活又整洁。

更多阅读:设计模式超简单解读(6):组合模式

什么是 TypeAdoption?

TypeAdoption 是一个 C# 源生成器库,它的核心功能是:

  • 通过在类的字段或属性上添加 [Adopt] 特性,自动生成对应接口的实现;
  • 将接口方法委托给被标记的内部成员;
  • 支持显式接口实现和隐藏接口,使内部成员既能复用接口,又能保持封装性。

简单来说,它帮你自动把内部对象的能力搬到外层类上,让类直接实现接口,而无需手动写大量委托方法。

核心概念

使用 TypeAdoption 之前,需要了解几个关键概念:

  • 源生成器:利用 Roslyn 编译器服务,在编译时生成额外的 C# 代码。
  • 委托模式:对象将其职责委托给另一个对象来执行。
  • 组合优于继承:通过组合其他对象获取功能,而不是通过继承基类。

基本用法

以日志包装器为例,假设我们有一个内部 ILogger<T> 对象,我们希望 ConfiguredLogger<T> 也实现 ILogger<T>,并将方法委托给内部对象:

using TypeAdoption;

public partial class ConfiguredLogger<T>(ILogger<T> innerLogger)
{
    [Adopt]
    private readonly ILogger<T> _innerLogger = innerLogger;

    public bool IsEnabled(LogLevel logLevel)
    {
        return logLevel > LogLevel.Debug;
    }

    // 其他 ILogger 成员自动委托给 _innerLogger
}

自动生成的委托代码

TypeAdoption 会在编译时生成如下代码:

public partial class ConfiguredLogger<T> : ILogger<T>
{
    public IDisposable? BeginScope<TState>(TState state) where TState : notnull
    {
        return _innerLogger.BeginScope<TState>(state);
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
                            Exception? exception, Func<TState, Exception?, string> formatter)
    {
        _innerLogger.Log(logLevel, eventId, state, exception, formatter);
    }
}

无需手动实现接口方法,所有未覆盖的方法都会自动委托给 _innerLogger

高级用法

当一个类实现多个接口,且接口方法签名可能冲突时,可以使用 显式接口实现,通过 [Adopt(Publicly = false)]

public partial class ConfiguredLogger<T>(ILogger<T> innerLogger, IConfiguration configuration)
{
    [Adopt]
    private readonly ILogger<T> _innerLogger = innerLogger;

    [Adopt(Publicly = false)]  // 生成显式接口实现
    private readonly IConfiguration _innerConfiguration = configuration;

    public bool IsEnabled(LogLevel logLevel)
    {
        return logLevel >= _innerConfiguration.GetMinLevel();
    }
}

生成的代码示例:

public partial class ConfiguredLogger<T> : ILoggerConfiguration
{
    LogLevel ILoggerConfiguration.GetMinLevel() => _innerConfiguration.GetMinLevel();
    string? ILoggerConfiguration.LogTag
    {
        get => _innerConfiguration.LogTag;
        set => _innerConfiguration.LogTag = value;
    }
    int ILoggerConfiguration.MinLevel
    {
        get => _innerConfiguration.MinLevel;
        set => _innerConfiguration.MinLevel = value;
    }
}

通过显式接口实现,可以避免接口方法冲突,同时保持内部成员封装性。

支持的成员类型

TypeAdoption 不仅支持方法委托,还支持:

  • 属性(Properties)
  • 事件(Events)
  • 泛型方法(Generic Methods)

因此,它可以广泛应用于各种接口和模式的实现场景。

工作原理

TypeAdoption 的工作流程大致如下:

  1. 源代码分析:扫描项目中带有 [Adopt] 特性的字段或属性。
  2. 语义分析:获取标记成员的类型信息以及接口定义。
  3. 代码生成:为每个需要委托的接口成员生成实现代码。
  4. 编译集成:生成的代码与原始类合并,形成完整的类定义。

核心源生成器逻辑通常在 AdoptionGenerator.cs 中实现,通过 Roslyn 的 IIncrementalGenerator 接口注册生成逻辑。

应用场景

1、日志包装器

可以在现有日志系统外层添加过滤、格式化或其他业务逻辑,而不改变原有接口实现。

2、数据访问层抽象

创建装饰器(Decorator)模式,实现缓存或审计逻辑:

public partial class CachedRepository<T>(IRepository<T> repository)
{
    [Adopt]
    private readonly IRepository<T> _repository = repository;

    public T GetById(int id)
    {
        // 缓存逻辑
        return _repository.GetById(id);
    }
}

3、策略模式实现

可以动态切换不同的行为实现,例如不同算法或处理逻辑。

性能优势

与运行时反射或动态代理相比,TypeAdoption 具有以下优势:

  1. 编译时生成:避免运行时反射开销。
  2. 直接调用:生成的方法直接调用内部成员,性能接近手写委托。
  3. 类型安全:编译时即可检查接口契约,减少潜在错误。

最佳实践

  • 使用 partial:TypeAdoption 依赖部分类机制生成代码。
  • 注意接口冲突:多个接口签名相同的情况,使用 Publicly = false
  • 保持简单:避免过度委托链,以免影响可读性。

与传统方案对比

方案 优点 缺点
手动实现接口委托 可完全控制实现 代码重复,维护成本高
运行时代理 / 反射 灵活,运行时可替换对象 性能低,缺少编译时检查
TypeAdoption 编译时生成,性能好,减少重复代码,类型安全 编译依赖增加,运行时灵活性较低

总结

TypeAdoption 为 C# 开发者提供了一种优雅的方式,实现接口委托与组合模式。

  • 自动生成接口实现,减少样板代码
  • 支持显式接口,实现封装和冲突解决
  • 性能接近手写委托,同时提供编译时类型安全

通过合理使用 TypeAdoption,我们可以构建 灵活、可维护、可扩展的系统,遵循 SOLID 原则中的单一职责原则和开闭原则。这种方法与良好的设计模式实践相结合,能有效提升代码质量。对这类技术实践感兴趣的开发者,欢迎在 云栈社区 交流讨论。

源码下载链接

[1] https://pan.baidu.com/s/1ymidTbyOivXca7htBGAC1w?pwd=vhm3




上一篇:使用OpenSCAD建模与3D打印技术为香氛瓶制作防滑底盘
下一篇:MySQL审核平台Yearning:8.8k Star开源项目的核心功能与部署指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-25 20:31 , Processed in 0.242707 second(s), 43 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表