美文网首页
C#手动拼装Expression,利用"泛型缓存"+"反射"实现

C#手动拼装Expression,利用"泛型缓存"+"反射"实现

作者: 浅谈码生活 | 来源:发表于2020-09-15 10:30 被阅读0次

首先,我们先认识一下Expression:Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;
Expression的定义为拉姆达表达式,但Expression本身是一种数据结构,就像对一个计算做了一个精确的描述,将其展开后会发现,分为“左边”及“右边”,每个元素都可以将值获取出来,所以本质可以理解为-二叉树。
1.表达式目录树可以通过compile 转换成一个委托

Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;//表达式目录树只能有一行;
var expFunc = exp.Compile();

2.手动拼装表达式目录树

Expression<Func<int>> expression = () => 123 + 234;  //两个常量相加-----表达式目录树的快捷声明
//转换手动拼接
Expression constant123 = Expression.Constant(123);
Expression constant234 = Expression.Constant(234);
Expression expressionAdd = Expression.Add(constant123, constant234); 
var exp = Expression.Lambda<Func<int>>(expressionAdd);
var func = exp.Compile();
int iResult = func.Invoke(); 

3.利用"泛型缓存"+"反射"实现类型硬编码转换

/// <summary>
/// 生成表达式目录树  泛型缓存
/// </summary>
/// <typeparam name="TIn"></typeparam>
/// <typeparam name="TOut"></typeparam>
public class ExpressionGenericMapper<TIn, TOut>//Mapper`2
{
    private static Func<TIn, TOut> _FUNC = null;
    static ExpressionGenericMapper()
    {
        ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
        List<MemberBinding> memberBindingList = new List<MemberBinding>();
        foreach (var item in typeof(TOut).GetProperties())
        {
            MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
            MemberBinding memberBinding = Expression.Bind(item, property);
            memberBindingList.Add(memberBinding);
        }
        foreach (var item in typeof(TOut).GetFields())
        {
            MemberExpression property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));
            MemberBinding memberBinding = Expression.Bind(item, property);
            memberBindingList.Add(memberBinding);
        }
        MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
        Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
        {
                parameterExpression
        });
        _FUNC = lambda.Compile();//拼装是一次性的
    }
    public static TOut Trans(TIn t)
    {
        return _FUNC(t);
    }
}

不太理解的朋友建议先了解泛型缓存机制。
4.拆分Expression表达式,组合SQL语句:

//1.准备Expression表达式目录树
Expression<Func<People, bool>> lambda = x => x.Age > 5 && x.Id > 5
                                                         && x.Name.StartsWith("1") //  like '1%'
                                                         && x.Name.EndsWith("1") //  like '%1'
                                                         && x.Name.Contains("1");//  like '%1%';

//2.继承目录树“访问者”抽象类,并对上述表达式包含的“属性”,“方法”,“常量”等方法进行重写:
public class ConditionBuilderVisitor : ExpressionVisitor
{
    private Stack<string> _StringStack = new Stack<string>();
    public string Condition()
    {
        string condition = string.Concat(this._StringStack.ToArray());
        this._StringStack.Clear();
        return condition;
    }

    /// <summary>
    /// 如果是二元表达式
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    protected override Expression VisitBinary(BinaryExpression node)
    {
        if (node == null) throw new ArgumentNullException("BinaryExpression");
        this._StringStack.Push(")");
        base.Visit(node.Right);//解析右边
        this._StringStack.Push(" " + node.NodeType.ToSqlOperator() + " ");
        base.Visit(node.Left);//解析左边
        this._StringStack.Push("(");
        return node;
    }

    /// <summary>
    /// 解析属性
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    protected override Expression VisitMember(MemberExpression node)
    {
        if (node == null) throw new ArgumentNullException("MemberExpression");
        //this._StringStack.Push(" [" + node.Member.Name + "] ");
        ////return node; 
        if (node.Expression is ConstantExpression)
        {
            var value1 = this.InvokeValue(node);
            var value2 = this.ReflectionValue(node);
            //this.ConditionStack.Push($"'{value1}'");
            this._StringStack.Push("'" + value2 + "'");
        }
        else
        {
            this._StringStack.Push(" [" + node.Member.Name + "] ");
        }
        return node;
    }
    private object InvokeValue(MemberExpression member)
    {
        var objExp = Expression.Convert(member, typeof(object));//struct需要
        return Expression.Lambda<Func<object>>(objExp).Compile().Invoke();
    }
    private object ReflectionValue(MemberExpression member)
    {
        var obj = (member.Expression as ConstantExpression).Value;
        return (member.Member as FieldInfo).GetValue(obj);
    }
    /// <summary>
    /// 常量表达式
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    protected override Expression VisitConstant(ConstantExpression node)
    {
        if (node == null) throw new ArgumentNullException("ConstantExpression");
        this._StringStack.Push(" '" + node.Value + "' ");
        return node;
    }
    /// <summary>
    /// 方法表达式
    /// </summary>
    /// <param name="m"></param>
    /// <returns></returns>
    protected override Expression VisitMethodCall(MethodCallExpression m)
    {
        if (m == null) throw new ArgumentNullException("MethodCallExpression");

        string format;
        switch (m.Method.Name)
        {
            case "StartsWith":
                format = "({0} LIKE {1}+'%')";
                break;

            case "Contains":
                format = "({0} LIKE '%'+{1}+'%')";
                break;

            case "EndsWith":
                format = "({0} LIKE '%'+{1})";
                break;

            default:
                throw new NotSupportedException(m.NodeType + " is not supported!");
        }
        this.Visit(m.Object);
        this.Visit(m.Arguments[0]);
        string right = this._StringStack.Pop();
        string left = this._StringStack.Pop();
        this._StringStack.Push(String.Format(format, left, right));
        return m;
    }
}

相关文章

  • C#手动拼装Expression,利用"泛型缓存"+"反射"实现

    首先,我们先认识一下Expression:Expression exp = (m, n) => m ...

  • 泛型

    ORM实现有反射、泛型、代码生成等几种常见方式,或者单用,或者混合。 c#的泛型非常强大,应用于ORM时,可能有些...

  • 泛型

    ORM实现有反射、泛型、代码生成等几种常见方式,或者单用,或者混合。 c#的泛型非常强大,应用于ORM时,可能有些...

  • 目录 - C#

    总目录 C# 第01局:泛型 C# 第02局:反射 C# 第03局:特性 C# 第04局:委托 C# 第05局:事...

  • C#基础提升系列——C#泛型

    C# 泛型(Generics) 泛型概述 泛型是C#编程语言的一部分,它与程序集中的IL(Intermediate...

  • C# 静态泛型缓存

    从赵劼 的博客中看的的方法,通过泛型建立一个泛型数据缓存,略加改进。闲话少说,上代码 最原始的样子 哈哈,就这么简...

  • 泛型 & 注解 & Log4J日志组件

    掌握的知识 : 基本用法、泛型擦除、泛型类/泛型方法/泛型接口、泛型关键字、反射泛型(案例) 泛型 概述 : 泛型...

  • Java泛型——利用反射越过泛型检查

    上一篇提到泛型基本是不可具化的,因为在运行期会将类型擦除。我们也知道泛型擦出的目的主要是为了兼容原生态类型的代码,...

  • 泛型

    普通类的多态 对泛型,是没有多态的 利用通配符,可以实现上面想实现的功能 泛型的嵌套 没有泛型数组,用通配符可以去...

  • 抽象工厂模式创建对象

    结合反射应用,使用class.forName反射创建对象,通过泛型约束参数类型新产品只需实现CommonAPI即可...

网友评论

      本文标题:C#手动拼装Expression,利用"泛型缓存"+"反射"实现

      本文链接:https://www.haomeiwen.com/subject/tvbxyktx.html