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方式将函数在运行时编译,且只编译一次。

浙公网安备 33010602011771号