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

1是一个节点,是一个常量类型的表达式,+号是一个节点,是一个二进制类型的表达式。在Expression中每个元素都是一个独立的节点,节点的拼接可以看似一个无线链接的树。
//创建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);
}
}
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 to sql、Beisen.Storage中使用Expression查询多租赁数据,也是使用 IQueryProvider 能力实现,IQueryProvider主要实现逻辑是使用Expression做为方法的参数,内部解析Expression转换为目标语言,如sql,webService,ESFilter等等。
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);
}
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;
}
// 其它成员
}
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();
}
}
//创建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方式将函数在运行时编译,且只编译一次。
对于^、:和"字符的特殊用途,我找不到很好的引用。 最佳答案 它匹配不是:或"的字符block。[...]-字符类-匹配此类中的字符。例如,[abc]将匹配一个字符,a或b或c。[^...]-否定字符类。+-匹配一个或多个另请参阅:CharacterClasses 关于ruby-什么([^:"]+)doinaRubyregularexpression?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/
我在Atom中使用Rubylinter,对于某些行,它会发出以下警告:(...)interpretedasgroupedexpression获取此警告的行示例如下:elsifnot(params[:vacancy].nil?orparams[:vacancy]['company_id'].nil?orparams[:vacancy]['company_id']=="0")应该如何改进该行以使警告消失? 最佳答案 警告是(...)interpretedasgroupedexpression它的意思就是它所说的:在Ruby中,圆括号可以
我刚刚阅读了有关asyncfunctions的内容,并发现了ES2017的一些类似功能。它造成了很多困惑,我只想问:asyncfunction、AsyncFunction(用于创建异步函数)和异步函数表达式(我认为这只是另一个异步函数)?什么时候应该使用一种格式而不是另一种格式?我们将不胜感激对每个怪癖和表现的强调! 最佳答案 在Javascript中有四种创建函数的方法。在Javascript中也有四种创建异步函数的方法,它们是彼此精确的镜像。为了演示这是如何工作的,我使用了一个简单的sleep函数,全局声明:functionsl
我设置了一个使用参数化路由的React-router:错误:SyntaxError:expectedexpression,got'我研究了这个错误,发现它是在服务器尝试获取.js/.css/other文件时发生的,但返回的HTML以开头。相反,所以我设置了express.static,但在输入URL时,例如comments/1250,它仍然返回:SyntaxError:expectedexpression,got'.这是我的服务器设置:app.use(express.static(__dirname+'/views/webpacked'));app.listen(5000);app.g
我正在做一个Angular项目,我需要根据一系列问题创建一个表单。我想为数组中的每个问题创建ng-model。所以我想出了类似下面的方法,但它不起作用。{{question.label}}:Required!有人可以帮我解决这个问题吗?提前致谢。 最佳答案 formQuickView[question.label].$error.required这是常规的JavaScript语法。您想要访问formQuickView的属性名称由question.label定义.更新不知何故我错过了要点,ng-model表达。基本上你在这里做同样的事
两者是否相同?假设你有:varx=true;然后你有一个:x&&doSomething();或if(x)doSomething();这两种语法之间有什么不同吗?我是不是偶然发现了一点糖? 最佳答案 严格来说,它们会产生相同的结果,但如果您将前一种情况用作其他情况的条件,则会得到不同的结果。这是因为在x&&doSomething()的情况下,doSomething()将返回一个值以表示其成功。 关于javascript-&&statement()是否与if()statement()相同?,
前文在阅读论文前,首先我们要有一定的知识储备,包括人脸建模,表情制作,旋转转换等,才能方便我们的论文理解,所以首先我会讲解一些关键的知识点。Flame模型的作用?Flame是一个3D人脸的通用模型,举个例子,你现在有一个特定人的3D人脸扫描序列,那么我便可以通过Flame模型拟合,构建个性化的模型,然后通过改变表情参数,动作参数,从而生成一些新的表情,动作的3D数据,以进行动画制作等。除此之外,因为扫描数据的误差和缺失,我可以通过Flame模型,对数据进行拟合平滑,得到较为完善,完美的3D数据。除此之外,我还可以使用通用的模板T,从而实现对人脸较为粗糙的3D重建。比如Deca中,通过深度学习与
在这里摆弄http://jsfiddle.net/prantikv/dJty6/36/我有这样的json数据$scope.info={"company1":"this","company2":"is","company3":"sparta"}我正在使用ng-repeat打印所有数据,我想监控字段的变化。我有一个像这样的monitorChange指令:.directive('monitorChange',function(){return{restrict:'A',scope:{changedFlag:'='},link:function(scope,element,attrs){var
在我发布我的问题之前,只想让您知道我进行了足够多的搜索,但找不到解决方案。这个问题让我很困惑。我有以下代码。首先ng-click正确地将ID插入函数中,但会产生Angular错误(在主题中提到)。第二次ng-click既不生成错误也不插入ID,而是呈现文字。我搜索了所有论坛,大多数人都提到像我的第二次ng-click一样使用它,但它对我不起作用。需要帮助!{{registration.id}}{{registration.dateModified|date}}ED回答:我做了一些测试,发现它让新手感到困惑,因为在FF或Chrome开发人员工具栏的HTML检查器中,您会看到代码将呈现DD
我正在尝试创建一个带有正则表达式的javascript函数来验证和格式化24小时时间,接受不带分号的时间并删除空格。例子:如果用户键入"0100"、"100"或"100",它将被接受但格式为"01:00"如果用户键入"01:00",它将被接受,无需格式化。谢谢。 最佳答案 functionformatTime(time){varresult=false,m;varre=/^\s*([01]?\d|2[0-3]):?([0-5]\d)\s*$/;if((m=time.match(re))){result=(m[1].length===