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);
        }
    }
}

源码位置:https://gitee.com/qiqigouwo/ObjAssignMap.git

posted @ 2020-09-21 11:04  一颗花生豆  阅读(922)  评论(0编辑  收藏  举报