C# Expression

Expression简介

表达式树又称为“表达式目录树”,以数据形式表示语言级代码,它是一种抽象语法树或者说是一种数据结构。

 简述一个Expression 表达式

 

 

1是一个节点,是一个常量类型的表达式,+号是一个节点,是一个二进制类型的表达式。在Expression中每个元素都是一个独立的节点,节点的拼接可以看似一个无线链接的树。

使用API方式创建一个表达式

//创建lambda表达式 num=>num>=5
//第一步创建输入参数,参数名为num,类型为int类型
ParameterExpression numParameter = Expression.Parameter(typeof(int), "num");
//第二步创建常量表达式5,类型int
ConstantExpression constant = Expression.Constant(5, typeof(int));
//第三步创建比较运算符>=,大于等于,并将输入参数表达式和常量表达式输入
//表示包含二元运算符的表达式。BinaryExpression继承自Expression
BinaryExpression greaterThanOrEqual = Expression.GreaterThanOrEqual(numParameter, constant);
//第四步构建lambda表达式树
//Expression.Lambda<Func<int, bool>>:通过先构造一个委托类型来创建一个 LambdaExpression
Expression<Func<int, bool>> lambda = Expression.Lambda<Func<int, bool>>(greaterThanOrEqual, numParameter);

整个表达式树节点的继承链

 

 

 

 

修改表达式树

现在我们想要修改表达式目录树,让它表示的Lambda表达式为(a,b)=>(a - (b * 2)),这时就需要编写自己的表达式目录树访问器,如下代码所示:

public class OperationsVisitor : ExpressionVisitor
{
    public Expression Modify(Expression expression)
    {
        return Visit(expression);
    }
 
    protected override Expression VisitBinary(BinaryExpression b)
    {
        if (b.NodeType == ExpressionType.Add)
        {
            Expression left = this.Visit(b.Left);
            Expression right = this.Visit(b.Right);
            return Expression.Subtract(left,right);
        }
 
        return base.VisitBinary(b);
    }
}

  

Linq之Expression

Expression最先接触的是使用Linq,使用Linq可以快速的创建一个简单的Expression

例如:

Func<int, int> func = num => num += 1;

这是一个数值加法的运算,输入一个数字进行+1之后返回。

经常在使用一个函数作为参数的时候我们会使用上述的方法,如果我想动态调整一个函数的时候,使用上述Func传递好像就行了,这个时候Expression就是我们最好的工具了。

Expression<Func<int, bool>> func2 = num2 => num2>= 1;

上述过程中使用lambda表达式创建一个最简单的LambdaExpression树。解析成树结构如下:

Expression<Func<int, int>> lambda = n =>
{
  int result = 0;
  for (int j = n; j >= 0; j--)
  {
     result += j;
  }
  return result;
};

  

上述代码,使用lambda形式创建了一个执行循环的表达式树,目前C#还不支持这样创建,如果想创建复杂的Expression,还需要使用API创建。

Linq 之 IQueryProvider & IQueryable<TData>

使用Linq to sql、Beisen.Storage中使用Expression查询多租赁数据,也是使用 IQueryProvider 能力实现,IQueryProvider主要实现逻辑是使用Expression做为方法的参数,内部解析Expression转换为目标语言,如sql,webService,ESFilter等等。

IQueryProvider 源码

public interface IQueryProvider
 {
   // 创建Object类型的IQueryable
   IQueryable CreateQuery(Expression expression);
 
  // 创建强类型的IQueryable
   IQueryable<TElement> CreateQuery<TElement>(Expression expression);
 
   //执行返回Object类型的计算
   object? Execute(Expression expression);
 
   //执行返回强类型的计算
   TResult Execute<TResult>(Expression expression);
 }

IQueryable源码

public interface IQueryable : IEnumerable
 {
   //当前查询使用的表达式树
   Expression Expression { get; }
 
   //当前循环数据类型
   Type ElementType { get; }
 
   //执行查询服务
   IQueryProvider Provider { get; }
 }

  

如果想让一个数据支持查询,那么必须实现IQueryable,上述源码中可以看到继承于IEnumerable,一个支持查询的数据接口,必须实现迭代器,也就是说明一个数据支持了Linq。

 

目前想要扩展Linq有两种方式,一个是使用IEnumerable,一个是使用IQueryable。

使用IEnumerable实现Linq

public class EnumerableData<T> : IEnumerable<T> where T : class
{
    public IEnumerator<T> GetEnumerator()
    {
        return null;
    }
 
    IEnumerator IEnumerable.GetEnumerator()
    {
        return null;
    }
 
    // 其它成员
}

实现一个QueryProvider

public class DemoQueryProvider : IQueryProvider
{
    public IQueryable CreateQuery(Expression expression)
    {
        Type elementType = expression.Type;
        try
        {
            return (IQueryable)Activator.CreateInstance(
                typeof(QueryableData<>).MakeGenericType(elementType),
                new object[] { this, expression });
        }
        catch
        {
            throw new Exception();
        }
    }
 
    public IQueryable<TResult> CreateQuery<TResult>(Expression expression)
    {
        return new QueryableData<TResult>(this, expression);
    }
 
    public object? Execute(Expression expression)
    {
        throw new NotImplementedException();
    }
 
    public TResult Execute<TResult>(Expression expression)
    {
        if (expression is MethodCallExpression methodCallEx)
        {
            var listValue = (methodCallEx.Arguments[0] as ConstantExpression)?.Value as QueryableData<string>;
            var method = (methodCallEx.Arguments[1] as UnaryExpression)?.Operand;
            var lambdaEx = (method as Expression<Func<string,string>>)?.Compile();
            return (TResult)Exec(listValue, lambdaEx);
        }
         
        if (expression is ConstantExpression { Value: TResult result })
        {
            return result;
        }
         
        return default(TResult);
 
    }
 
    public IEnumerable<string> Exec(QueryableData<string> listValue,Func<string,string> lambdaEx)
    {
        //这里换成列表类型,为了防止循环使用QueryableData 的GetEnumerator,导致死循环
        foreach (var item in listValue.ToList())
        {
            yield return lambdaEx(item);
        }
    }
}

 

public class QueryableData<TData> : List<TData>,IQueryable<TData>
{
    public QueryableData()
    {
        Provider = new DemoQueryProvider();
        Expression = Expression.Constant(this);
    }
 
    public QueryableData(DemoQueryProvider provider,
        Expression expression)
    {
        if (provider == null)
        {
            throw new ArgumentNullException("provider");
        }
 
        if (expression == null)
        {
            throw new ArgumentNullException("expression");
        }
 
        if (!typeof(IQueryable<TData>).IsAssignableFrom(expression.Type))
        {
            throw new ArgumentOutOfRangeException("expression");
        }
 
        Provider = provider;
        Expression = expression;
    }
     
    public IQueryProvider Provider { get; private set; }
    public Expression Expression { get; private set; }
 
    public Type ElementType
    {
        get { return typeof(TData); }
    }
 
    public IEnumerator<TData> GetEnumerator()
    {
        return (Provider.Execute<IEnumerable<TData>>(Expression)).GetEnumerator();
    }
 
    IEnumerator IEnumerable.GetEnumerator()
    {
        return (Provider.Execute<IEnumerable>(Expression)).GetEnumerator();
    }
}

  

Expression &Reflection.Emit

//创建lambda表达式 num=>num>=5
//第一步创建输入参数,参数名为num,类型为int类型
ParameterExpression numParameter = Expression.Parameter(typeof(int), "num");
//第二步创建常量表达式5,类型int
ConstantExpression constant = Expression.Constant(5, typeof(int));
//第三步创建比较运算符>=,大于等于,并将输入参数表达式和常量表达式输入
//表示包含二元运算符的表达式。BinaryExpression继承自Expression
BinaryExpression greaterThanOrEqual = Expression.GreaterThanOrEqual(numParameter, constant);
//第四步构建lambda表达式树
//Expression.Lambda<Func<int, bool>>:通过先构造一个委托类型来创建一个 LambdaExpression
Expression<Func<int, bool>> lambda = Expression.Lambda<Func<int, bool>>(greaterThanOrEqual, numParameter);

  

这是上述的一个Demo,使用ExpressionAPI手动创建了一个表达式树,如果想要执行这段程序,还需要编译一下,LambdaExpression独有的方法Compile,接下来看看Compile中做了什么。

public new TDelegate Compile()
{
#if FEATURE_COMPILE
            return (TDelegate)(object)Compiler.LambdaCompiler.Compile(this);
#else
            return (TDelegate)(object)new Interpreter.LightCompiler().CompileTop(this).CreateDelegate();
#endif
}
 
 
internal sealed class LightDelegateCreator
  {
    private readonly LambdaExpression _lambda;
 
    internal LightDelegateCreator(System.Linq.Expressions.Interpreter.Interpreter interpreter, LambdaExpression lambda)
    {
      this.Interpreter = interpreter;
      this._lambda = lambda;
    }
 
    internal System.Linq.Expressions.Interpreter.Interpreter Interpreter { get; }
 
    public Delegate CreateDelegate() => this.CreateDelegate((IStrongBox[]) null);
 
    internal Delegate CreateDelegate(IStrongBox[] closure) => new LightLambda(this, closure).MakeDelegate(this._lambda.Type);
  }
 
 
//LightLambda.MakeDelegate
internal Delegate MakeDelegate(Type delegateType) => delegateType.GetInvokeMethod().ReturnType == typeof (void) ? DelegateHelpers.CreateObjectArrayDelegate(delegateType, new Func<object[], object>(this.RunVoid)) : DelegateHelpers.CreateObjectArrayDelegate(delegateType, new Func<object[], object>(this.Run));
 
 
//使用emit创建数组类型的委托,实际就是DynamicMethod
internal static Delegate CreateObjectArrayDelegate(Type delegateType,Func<object[], object> handler)
{
   return DelegateHelpers.CreateObjectArrayDelegateRefEmit(delegateType, handler);
}
private static Delegate CreateObjectArrayDelegateRefEmit(
     Type delegateType,
     Func<object[], object> handler)
   {
     MethodInfo methodInfo;
     if (!DelegateHelpers.s_thunks.TryGetValue(delegateType, out methodInfo))
     {
       MethodInfo invokeMethod = delegateType.GetInvokeMethod();
       Type returnType = invokeMethod.ReturnType;
       bool hasReturnValue = returnType != typeof (void);
       ParameterInfo[] parametersCached = invokeMethod.GetParametersCached();
       methodInfo = DelegateHelpers.GetCSharpThunk(returnType, hasReturnValue, parametersCached);
       if (methodInfo == (MethodInfo) null)
       {
         int num = Interlocked.Increment(ref DelegateHelpers.s_ThunksCreated);
         Type[] parameterTypes = new Type[parametersCached.Length + 1];
         parameterTypes[0] = typeof (Func<object[], object>);
         StringBuilder stringBuilder = new StringBuilder();
         stringBuilder.Append("Thunk");
         stringBuilder.Append(num);
         if (hasReturnValue)
         {
           stringBuilder.Append("ret_");
           stringBuilder.Append(returnType.Name);
         }
         for (int index = 0; index < parametersCached.Length; ++index)
         {
           stringBuilder.Append('_');
           stringBuilder.Append(parametersCached[index].ParameterType.Name);
           parameterTypes[index + 1] = parametersCached[index].ParameterType;
         }
         DynamicMethod dynamicMethod = new DynamicMethod(stringBuilder.ToString(), returnType, parameterTypes);
         methodInfo = (MethodInfo) dynamicMethod;
         ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
         LocalBuilder local1 = ilGenerator.DeclareLocal(typeof (object[]));
         LocalBuilder local2 = ilGenerator.DeclareLocal(typeof (object));
         if (parametersCached.Length == 0)
         {
           ilGenerator.Emit(OpCodes.Call, DelegateHelpers.s_ArrayEmpty);
         }
         else
         {
           ilGenerator.Emit(OpCodes.Ldc_I4, parametersCached.Length);
           ilGenerator.Emit(OpCodes.Newarr, typeof (object));
         }
         ilGenerator.Emit(OpCodes.Stloc, local1);
         bool flag = false;
         for (int index = 0; index < parametersCached.Length; ++index)
         {
           bool isByRef = parametersCached[index].ParameterType.IsByRef;
           Type type = parametersCached[index].ParameterType;
           if (isByRef)
             type = type.GetElementType();
           flag |= isByRef;
           ilGenerator.Emit(OpCodes.Ldloc, local1);
           ilGenerator.Emit(OpCodes.Ldc_I4, index);
           ilGenerator.Emit(OpCodes.Ldarg, index + 1);
           if (isByRef)
             ilGenerator.Emit(OpCodes.Ldobj, type);
           Type boxableType = DelegateHelpers.ConvertToBoxableType(type);
           ilGenerator.Emit(OpCodes.Box, boxableType);
           ilGenerator.Emit(OpCodes.Stelem_Ref);
         }
         if (flag)
           ilGenerator.BeginExceptionBlock();
         ilGenerator.Emit(OpCodes.Ldarg_0);
         ilGenerator.Emit(OpCodes.Ldloc, local1);
         ilGenerator.Emit(OpCodes.Callvirt, DelegateHelpers.s_FuncInvoke);
         ilGenerator.Emit(OpCodes.Stloc, local2);
         if (flag)
         {
           ilGenerator.BeginFinallyBlock();
           for (int index = 0; index < parametersCached.Length; ++index)
           {
             if (parametersCached[index].ParameterType.IsByRef)
             {
               Type elementType = parametersCached[index].ParameterType.GetElementType();
               ilGenerator.Emit(OpCodes.Ldarg, index + 1);
               ilGenerator.Emit(OpCodes.Ldloc, local1);
               ilGenerator.Emit(OpCodes.Ldc_I4, index);
               ilGenerator.Emit(OpCodes.Ldelem_Ref);
               ilGenerator.Emit(OpCodes.Unbox_Any, elementType);
               ilGenerator.Emit(OpCodes.Stobj, elementType);
             }
           }
           ilGenerator.EndExceptionBlock();
         }
         if (hasReturnValue)
         {
           ilGenerator.Emit(OpCodes.Ldloc, local2);
           ilGenerator.Emit(OpCodes.Unbox_Any, DelegateHelpers.ConvertToBoxableType(returnType));
         }
         ilGenerator.Emit(OpCodes.Ret);
       }
       DelegateHelpers.s_thunks[delegateType] = methodInfo;
     }
     return methodInfo.CreateDelegate(delegateType, (object) handler);
   }

查看上述链路最后到方法CreateObjectArrayDelegateRefEmit,此方法中使用Emit创建一个叫DynamicMethod的类型。所以lambda中最终使用的是Emit方式将函数在运行时编译,且只编译一次。

posted @ 2023-02-17 17:22  ThirteenAnimation  阅读(104)  评论(0编辑  收藏  举报