场景
刚写完一个干净利落的方法,比如保存数据到数据库,逻辑清晰、结构优雅,
第二天,“嘿,保存完数据,记得给客户发个邮件哦~”
第三天,“能不能再发个消息通知其他系统?”
第四天,“能不能记录一下操作日志?”
第五天,“再加个短信提醒吧。”
……
就这样,原本清清爽爽的 SaveData 方法,变成了一个臃肿不堪的函数:
我们管这种代码叫 “脚本代码”或“面条代码” —— 逻辑缠在一起,改一处,处处提心吊胆。
C# 提供了更灵活的方式来处理这种场景,那就是利用 Attribute 来对业务进行解耦,从而避免这种脚本式的代码,提高代码的可扩展性
1. 定义特性
namespace WebApplication2.Attributes
{
/// <summary>
/// https://mp.weixin.qq.com/s/Sd9q7FOTlk29wBknNQh87w
/// 后置操作特性基类
/// 所有继承它的特性都可以用在方法上,允许多个,不继承到子类
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
public abstract class PostOperationAttribute : Attribute
{
// 每个后置操作都必须实现 Execute 方法
public abstract void Execute(object returnValue);
}
/// <summary>
/// 发送邮件特性
/// </summary>
public class SendEmailAttribute : PostOperationAttribute
{
private readonly string _emailTemplate; // 邮件模板名称
// 构造函数接收模板名
public SendEmailAttribute(string emailTemplate)
{
_emailTemplate = emailTemplate;
}
// 实现具体的发送邮件逻辑
public override void Execute(object returnValue)
{
// 实际项目中这里应该调用邮件服务
Console.WriteLine($"发送邮件 - 使用模板: {_emailTemplate}");
Console.WriteLine($"邮件内容包含数据: {returnValue}");
}
}
/// <summary>
/// 发送消息特性
/// </summary>
public class SendMessageAttribute : PostOperationAttribute
{
private readonly string _messageType; // 消息类型
public SendMessageAttribute(string messageType)
{
_messageType = messageType;
}
public override void Execute(object returnValue)
{
Console.WriteLine($"发送 {_messageType} 消息");
Console.WriteLine($"消息内容包含数据: {returnValue}");
}
}
}
2. 编写业务方法
using System.Reflection;
namespace WebApplication2.Attributes
{
public class DataService
{
/// <summary>
/// 核心逻辑只负责保存数据
/// 使用特性标记需要后置处理的方法
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
[SendEmail("DataSavedTemplate")]
[SendMessage("Notification")]
public virtual int SaveData(string data)
{
// 这里只关注保存数据的核心业务逻辑
Console.WriteLine($"保存数据: {data}");
// 模拟返回保存后的ID
return new Random().Next(1000);
}
}
}
3. 创建拦截类(代理)
using System.Reflection;
namespace WebApplication2.Attributes
{
/// <summary>
/// 自动处理 Attribute 的代理类
/// </summary>
public class DataServiceProxy : DataService
{
public override int SaveData(string data)
{
// 调用基类方法
var result = base.SaveData(data);
// 获取方法信息
MethodInfo methodInfo = typeof(DataService).GetMethod("SaveData");
// 获取该方法上所有的 PostOperationAttribute 特性实例
var postOps = methodInfo.GetCustomAttributes<PostOperationAttribute>(true);
// 遍历并执行每一个后置操作
foreach (var op in postOps)
{
op.Execute(result);
}
return result;
}
}
}
4. 使用
using Microsoft.AspNetCore.Mvc;
using WebApplication2.Attributes;
namespace WebApplication2.Controllers
{
[Route("api/Attributes/[action]")]
[ApiController]
public class AttributesController : ControllerBase
{
[HttpGet]
public string Test()
{
// 使用代理类而不是直接使用DataService
var dataService = new DataServiceProxy();
// 直接调用方法,后置操作会自动执行
int savedId = dataService.SaveData("测试数据");
Console.WriteLine($"保存成功,ID: {savedId}");
return "";
}
}
}
5. 运行和测试

6.总结
● 核心业务方法不再被新增加的业务需求污染
● 扩展功能就像搭积木一样快捷方便
● 新增功能无需修改原有代码,维护成本大大降低
● 一眼就能看出某个方法执行后会触发哪些操作,代码可读性更强
3692

被折叠的 条评论
为什么被折叠?



