C# 装饰器模式实现方法 C#如何动态地给对象添加功能

1次阅读

装饰器模式在c#中通过接口+组合+委托实现,所有组件实现同一接口,装饰器持有该接口实例并委托调用;避免继承,支持运行时动态叠加;需警惕链断裂和无限递归;不同于Attribute或dispatchproxy等代理机制,它类型安全、调试友好但依赖显式接口契约。

C# 装饰器模式实现方法 C#如何动态地给对象添加功能

装饰器模式在 C# 中的核心实现思路

装饰器模式不是靠语言特性(比如 Python 的 @decorator)自动支持的,而是靠接口 + 组合 + 委托来手动构造。关键在于:所有装饰器和被装饰对象必须实现同一接口,装饰器内部持有一个该接口类型的实例,并在方法调用中“包装”它。

典型结构包括:IComponent(统一接口)、ConcreteComponent(原始实现)、Decorator(抽象基类,含 IComponent 字段)、多个具体装饰器(如 LoggingDecoratorRetryDecorator)。

如何用组合代替继承实现动态增强

避免用继承扩展行为——那会提前绑定、无法运行时叠加。装饰器通过构造函数接收 IComponent,把“增强逻辑”写在方法体内,再显式调用 _inner.DoSomething(),从而控制执行时机(比如前置日志、后置清理、异常重试)。

  • LoggingDecorator 可在调用前记录开始时间,调用后记录耗时和结果
  • RetryDecorator 可捕获异常并循环重试,直到成功或超限
  • 多个装饰器可嵌套:new RetryDecorator(new LoggingDecorator(new HttpService()))

常见错误:装饰器链断裂或无限递归

最容易出问题的是装饰器内部没调用 _inner,或者误调了 this.DoSomething() 导致溢出。C# 编译器不会报错,但运行时直接崩溃。

检查点:

  • 每个装饰器的构造函数必须接收并保存 IComponent 类型参数,不能是具体类型(否则破坏多态
  • 所有方法实现中,必须明确调用 _inner.SomeMethod(),而不是 this.SomeMethod()
  • 避免在 ToString()GetHashCode() 等基础方法里意外触发装饰逻辑(除非有意为之)

与 .NET 原生机制(如 Attribute 或 AOP)的区别

C# 的 [Attribute] 本身不执行逻辑,只是元数据;真正拦截调用需要配合 RealProxy(已废弃)、DispatchProxy(.NET Core+)或第三方库(如 Castle DynamicProxy)。这些属于运行时代理,和装饰器模式有本质不同:

  • 装饰器是显式构造、类型安全、调试友好,但需手动组合
  • 代理是隐式织入、无需改调用方代码,但栈深、难调试、部分场景不支持(如 sealed 类、非虚方法)
  • 若你只需要日志/授权等横切关注点,DispatchProxy 更轻量;若需精细控制每层行为顺序或状态传递(比如带上下文的缓存装饰器),手写装饰器更可靠

别指望给任意已有类“一键加装饰器”——C# 没有 ruby 那种开放类机制,所有装饰都建立在接口契约之上。漏掉接口定义,整个模式就跑不起来。

text=ZqhQzanResources