前言
眾所周知,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);
}
}
}