当前位置: 首页 > news >正文

对实体类Id自增

我们首先需要明白

lambda 表达式  (item=>item.Id)↓
Expression 树   →  检查是属性访问↓
Expression.Assign → 拼出 “item.Id = index”↓
Compile() → 生成机器码委托  Action<T,int>↓
放进 ConcurrentDictionary 缓存,终身复用

在原生的Linq中,我们可以写

List<Student> Students=CreateStudent();
Students.Select((item, index) => { item.Id = index; return item; });

我们可以自定义一个

Students.SelectWithIndex(item=>item.Id);
  /// <summary>/// 对序列按索引赋值,指定成员表达式 item => item.Id/// </summary>public static IEnumerable<T> SelectWithIndex<T>(this IEnumerable<T> source, Expression<Func<T, int>> memberSelector){if (source == null) throw new ArgumentNullException(nameof(source));if (memberSelector == null) throw new ArgumentNullException(nameof(memberSelector));// 编译一次 settervar setter = GetOrCreateSetter(memberSelector);return source.Select((item, idx) =>{setter(item, idx);return item;});}// 内部:把 Expression<item.Id> 编译成 Action<item,index>private static readonly ConcurrentDictionary<(Type, string), Delegate> _cache = new ConcurrentDictionary<(Type, string), Delegate>();
// ① 这是一个“编译并缓存”的工厂方法:输入 lambda (item=>item.Id) ,输出 Action<T,int> 委托
//    泛型 T 就是你要处理的模型类型,例如 MotionCalibrationModel
private static Action<T, int> GetOrCreateSetter<T>(Expression<Func<T, int>> selector)
{// ② 拿到 T 的运行时类型信息,后面要用它拼缓存键var type = typeof(T);// ③ 拼一个复合键:(类型, lambda 主体字符串)//    例如 (MotionCalibrationModel, "item.Id")  这样同一个类不同属性也能分开缓存var key = (type, selector.Body.ToString());// ④ ConcurrentDictionary 的 GetOrAdd 是线程安全的“获取或添加”//    如果缓存里已有这个键,直接返回已编译好的委托;否则执行 lambda 表达式编译逻辑return (Action<T, int>)_cache.GetOrAdd(key, _ =>{/* ⑤ 下面是“编译”过程,只做一次,以后复用 */// ⑥ 确保 lambda  body 是 item.Id  这种“取属性”形式if (!(selector.Body is MemberExpression mem) ||!(mem.Member is PropertyInfo prop) ||!prop.CanWrite ||                 // 必须有 set 访问器prop.PropertyType != typeof(int)) // 必须是 intthrow new ArgumentException("Lambda 必须返回一个可写的 int 属性,如 item => item.Id");// ⑦ 手工构造两个参数表达式//    paramItem  代表“将来传进来的对象”//    paramIndex 代表“将来传进来的索引值”var paramItem = Expression.Parameter(type, "item");var paramIndex = Expression.Parameter(typeof(int), "index");// ⑧ 构造赋值表达式:  item.Id = index//    左侧是“取属性”,右侧是“索引值”var assign = Expression.Assign(Expression.Property(paramItem, prop), paramIndex);// ⑨ 把赋值表达式包成 Lambda ://    (item, index) => item.Id = index//    Compile() 之后变成真正的委托 Action<T,int>return Expression.Lambda<Action<T, int>>(assign, paramItem, paramIndex).Compile();});
}
http://www.hskmm.com/?act=detail&tid=10714

相关文章:

  • HarmonyOS之UIContext 与 UIAbility、WindowStage 的关系 - 指南
  • 向上一步——当做事纠结的人停止决策内耗,你就是掌控自己的神!
  • Windows平台安装cocos2d-x V3.17.2
  • 完整教程:Mistral Document AI已正式登陆Azure AI Foundry(国际版)
  • 飞书机器人推送消息通知用自定义机器人
  • ENSP 常用命令
  • Kubernetes权威指南-基础篇
  • 【IEEE出版、已连续6届EI稳定检索】第七届机器学习、大数据与商务智能国际会议 (MLBDBI 2025)
  • office2024安装教程(附安装包)Office 2024 专业增强版下载安装激活详细图文步骤
  • Gitflow 工作流程
  • 魔改chromium真正通杀全网debugger检测
  • C#依赖注入
  • 完整教程:Docker Compose 一键启动多容器服务
  • 【截稿倒计时、高录用、稳检索】2025年教育创新与信息技术国际学术会议(EIIT 2025)
  • 低代码 + AI 构建智慧校园系统:某高校宿舍报修平台的48小时构建全流程
  • [MCP][07]logging和progress等功能说明
  • 端口命令
  • Microsoft OLE漏洞致远程代码执行安全公告解析
  • 写代码还是写提示词?——Prompt 工程是不是程序员的新技能树
  • c-store发送dcm文件超时
  • 解码C语言模块化编程
  • redis存储漂流瓶信息
  • hashcat高效爆破Wi-Fi密码方法(比aircrack-ng快)
  • 更新到macOS Sequoia后,chrome无法用ip访问192.168.*
  • Typora标题自动显示序号,大纲中也显示序号
  • 【IEEE出版、格林威治大学主办】第六届信息科学与并行、分布式处理国际学术会议(ISPDS 2025)
  • Day18面向对象的基本认识与回顾方法的定义
  • 【2025-09-19】连岳摘抄
  • 【2025-09-18】工作情绪
  • Ubuntu 系统部署 LNMP 环境详细教程(附shell脚本一键部署↓) - 指南