C# .NET6中如何编写可在方法前后执行逻辑的Attribute特性
可行实现方案
优先选择和JSON反序列化流程适配的原生方案,不需要额外改造构造逻辑,也不需要外部手动调用初始化方法。
方案1:使用反序列化完成回调(最适配当前需求)
主流.NET JSON序列化库(System.Text.Json、Newtonsoft.Json)都原生支持反序列化生命周期回调,标记对应特性的方法会在「对象构造完成、所有字段/属性赋值完毕」后自动执行,完美避开构造函数内执行初始化时字段还是默认值的问题。
示例代码:
using System.Runtime.Serialization; // 若使用Newtonsoft.Json,无需额外命名空间,同名特性可直接兼容 public class Spam { public int stored_var = 1; private int evaluated_var; public Spam() { } public Spam(int a) { this.stored_var = a; } [OnDeserialized] private void AfterObjectCreated(StreamingContext context = default) { // 此处执行时,无论是反序列化赋值还是构造函数传参赋值都已完成 this.Initialize(); } public void Initialize() { this.evaluated_var = this.stored_var + 1; } }
适配说明:
- 反序列化场景:无参构造执行完、JSON字段全部填充到对象后,会自动触发该方法
- 手动new对象场景:带参构造中参数赋值完成后,可直接调用该回调方法,逻辑完全内聚在类内部,不需要外部调用者感知
方案2:实现类似Python装饰器的通用构造拦截
如果需要完全不区分创建场景,所有实例(不管是new出来的、反序列化出来的、反射创建的)都自动在构造完成后执行Initialize,可以通过以下两种方式实现你要的[InitializeCaller]特性效果:
- 编译时代码注入(Source Generator):自定义
InitializeCaller特性,编写源生成器在编译阶段扫描所有标记该特性的类,自动生成分部类代码:在所有构造逻辑、字段赋值逻辑执行完成后插入Initialize调用;也可以将构造函数封装为静态工厂方法,在工厂方法中完成「构造实例→赋值→调用Initialize」的全流程,对调用方完全透明。 - 运行时动态代理:使用动态代理库拦截所有目标类的实例创建过程,在代理逻辑中当构造函数执行完成、实例返回前自动调用Initialize方法。这种方式和Python装饰器的运行时拦截逻辑完全一致,缺点是必须通过代理类创建实例,无法直接new,且不支持密封类。
方案3:值变更触发+延迟初始化(零依赖通用方案)
如果不想依赖任何第三方库或框架特性,可以直接把初始化逻辑和字段访问/赋值绑定,不需要关心对象的创建流程:
public class Spam { private int _stored_var = 1; private int _evaluated_var; private bool _isInitialized; public int stored_var { get => _stored_var; set { _stored_var = value; Initialize(); } } public Spam() { } public Spam(int a) { _stored_var = a; } public int GetEvaluatedValue() { if (!_isInitialized) Initialize(); return _evaluated_var; } public void Initialize() { _evaluated_var = _stored_var + 1; _isInitialized = true; } }
这种方案下,不管对象是通过什么方式创建的:
- 反序列化时给
stored_var赋值会自动触发Initialize - 手动new传参赋值后,第一次访问计算值时会自动补做初始化
- 后续字段值变更时会自动重算依赖值,不会出现数据不一致
注意:C#原生的Attribute仅作为元数据挂载在类型/成员上,本身不具备执行拦截能力,不存在只标记Attribute就自动在构造后执行逻辑的原生实现,必须配合上述的生命周期回调、编译时注入或运行时代理才能生效。
内容的提问来源于stack exchange,提问作者Dario
相关产品推荐
相关产品推荐

