C# 实现根据字段规则的赋值器(基于 Lambda 表达式树实现)
前言
众所周知,C#中最快的莫过于 lambda 和 emit。在很多场景下,多个实体类型具有相同规则的字段,例如:user_createtime,role_createtime,这两个字段都表示创建时间,还有 user_audtflag,role_audtflag,都表示审核标识。它们都是由一个前缀加上固定的名称所构成,具有一定规则。如果这样的字段很多,而又不想每次手动对它们都赋值,首先想到的是利用反射实现。然而反射的性能是比较慢的。所以今天给大家用 lambda 表达式树实现一个简单的赋值器
源码位置
ObjAssignMap:https://gitee.com/qiqigouwo/ObjAssignMap.git
安装方法
PM> Install-Package ObjAssignMap
或者 dotnet add package ObjAssignMap
简单使用
准备实体类
public class User
{
public string ur_Name {get;set;}
public DateTime? ur_audttime {get;set;}
public string ur_audtman {get;set;}
public string ur_audtflag {get;set;}
}
创建配置
public class AssignOption
{
public const string Audt = "audt";//审核分组名
public const string UnAudt = "unaudt";//弃审分组名
//配置
public static IObjAssign CreateAssign()
{
var config = new AssignConfig();
//审核日期
config.ForField<DateTime?>(Audt,UnAudt)//规则应用于 Audt 和 UnAudt 分组
.SetRule(field => field.EndsWith("_audttime"))
.SetVal(p => DateTime.Now);
//审核人
config.ForField<string>(Audt,UnAudt)
.SetRule(field => field.EndsWith("_audtman"))
.SetVal(p => GetUserID());
//审核标志
config.ForField<string>(Audt)//字段只应用于 Audt 分组
.SetRule(field => field.EndsWith("_audtflag"))
.SetVal(p => "1");
//审核标志
config.ForField<string>(UnAudt)
.SetRule(field => field.EndsWith("_audtflag"))
.SetVal(p => "0");
return config.Build();
}
}
使用
var user = new User();
var assign = AssignOption.CreateAssign();//实际使用中,需要设置为单实例
assign.Assign(user, AssignOption.Audt);
//assign.AssignObject(user, AssignOption.Audt);
/* 最后输出结果
* user.ur_audttime => 会调动 DateTime.Now 或获取时间
* user.ur_audtman => 会调用 GetUserID获取用户ID
* user.ur_audtflag => "1"
*/
核心源码
首次对类型进行赋值时,会根据配置的字段规则进行匹配,并且构建表达式,编译为委托,缓存到_map
中,后面使用时就从缓存中取,不会再次构建。注意:委托的性能仅次于 C#源码
private void AddAssignDet<T>(string groupName)
where T : class
{
_ = groupName ?? throw new ArgumentNullException(nameof(groupName));
var type = typeof(T);
var key = GetMapKey(type, groupName);
lock (this)
{
if (_map.ContainsKey(key)) return;
var fields = type.GetProperties();
var p = Expression.Parameter(type, "p");
var binds = new List<BinaryExpression>();
var gp = this._rules.Where(x => x.GroupNames.Contains(groupName)).ToList();
foreach (var rule in gp)
{
foreach (var field in fields)
{
if (rule?.Rule?.Invoke(field.Name) ?? false && rule.ValueType == field.PropertyType)
{
MemberExpression member = Expression.Property(p, field);//left 字段
MethodCallExpression method = Expression.Call(rule?.ValueExpress, rule?.ValueMethod, member);//right 委托
BinaryExpression binary = Expression.Assign(member, method);//组合赋值
binds.Add(binary);
}
}
}
if (binds.Any())
{
_map.TryAdd(key, Expression.Lambda<Action<T>>(Expression.Block(binds), p).Compile());
}
else
{
_map.TryAdd(key, null);
}
}
}