如何分解会员访问表达链?简短版本(TL;DR):假设我有一个表达式,它只是一个成员访问运算符链:Expressione=x=>x.foo.bar.baz;你可以把这个表达式想象成一个子表达式的组合,每个子表达式包含一个成员访问操作:Expressione1=(Txx)=>x.foo;表达式e2=(Tfoofoo)=>foo.bar;表达式>e3=(Tbarbar)=>bar.baz;我想要做的是分解这些组件子表达式,以便我可以单独使用它们。更简短的版本:如果我有表达式x=>x.foo.bar,我已经知道如何打破x=>x.foo。如何提取另一个子表达式foo=>foo.bar?我为什么这样做:我试图在C#中模拟“提升的”成员访问运算符,例如CoffeeScript的存在访问运算符?。.EricLippert表示,曾考虑为C#使用类似的运算符,但没有实施它的预算。如果C#中存在这样的运算符,您可以这样做:value=target?.foo?.bar?.baz;如果target.foo.bar.baz链的任何部分结果为null,整个事情将评估为null,从而避免NullReferenceException。我想要一个可以模拟这种事情的Lift扩展方法:value=target.Lift(x=>x.foo.bar.baz);//返回target.foo.bar.baz或null但是,它是不完整的,因为我只知道如何保留成员访问表达式的左侧。我可以将x=>x.foo.bar.baz变成x=>x.foo.bar,但我不知道如何保留bar=>bar.baz。所以它最终会做这样的事情(伪代码):return(x=>x)(target)==null?空:(x=>x.foo)(目标)==空?null:(x=>x.foo.bar)(target)==null?空:(x=>x.foo.bar.baz)(目标);这意味着表达式中最左边的步骤会被一遍又一遍地求值。如果它们只是POCO对象的属性可能没什么大不了的,但是将它们转换为方法调用时,效率低下(和潜在的副作用)变得更加明显://仍然是伪代码return(x=>x())(target)==空?null:(x=>x().foo())(target)==null?null:(x=>x().foo().bar())(target)==null?null:(x=>x().foo().bar().baz())(目标);代码:staticTResultLift(thisTtarget,Expressionexp)whereTResult:class{//省略:如果target可以为null&&target==null,则返回nullvarmemberExpression=exp.BodyasMemberExpression;if(memberExpression!=null){//如果memberExpression是{x.foo.bar},则innerExpression是{x.foo}varinnerExpression=memberExpression.Expression;varinnerLambda=Expression.Lambda(innerExpression,exp.Parameters);如果(target.Lift(innerLambda)==null){返回null;}else{////这是我坚持的部分。可能的伪代码://varmember=memberExpression.Member;//返回GetValueOfMember(target.Lift(innerLambda),成员);}}//现在,我我坚持这个:returnexp.Compile()(target);这个答案松散地启发了这个提升方法的替代方案,以及为什么我不能使用它们:possiblemonadvalue=x.ToMaybe().Bind(y=>y.foo).Bind(f=>f.bar).Bind(b=>b.baz).Value;优点:除了提升成员访问权限外,还使用函数式编程中流行的现有模式还有其他用途缺点:过于冗长。我不想每次想钻取几个成员时都进行大量的函数调用。即使我实现SelectMany并使用查询语法,恕我直言,它看起来会更混乱,而不是更少。我必须手动覆盖x.foo.bar.baz作为它们各自的组件,这意味着我必须在编译时知道它们是什么。我不能只使用result=Lift(expr,obj);对于像result=Lift(expr,obj)这样的变量;不是真正的设计,我正在尝试做的事情,感觉并不完美。ExpressionVisitor我将IanGriffith的LiftMemberAccessToNull方法修改为可以像我描述的那样使用的通用扩展方法。代码太长,无法包含在此处,但如果有人感兴趣,我会发布Gist。优点:遵循result=target.Lift(x=>x.foo.bar.baz)语法如果链中的每个步骤都返回引用类型或不可为null的值类型,则效果很好缺点:如果Any成员可以为nullvalue类型并且它令人窒息,这实际上限制了它对我的用处。我需要它为Nullable成员工作。尝试捕捉try{value=x.foo.bar.baz;}catch(NullReferenceExceptionex){value=null;这是最明显的方式,如果找不到更优雅的方式,我会使用它。优点:很简单。很明显代码是什么。我不必担心边缘情况。缺点:它丑陋而冗长try/catch块是一个非常重要的*性能打击它是一个语句块,所以我无法让它为LINQ发出表达式树感觉就像承认失败我不会说谎;“不承认失败”是我如此固执的主要原因。我的直觉告诉我必须有一种优雅的方式来做到这一点,但找到它一直是一个挑战。我不敢相信表达式的左侧很容易访问它,但右侧几乎无法访问。我确实有两个问题,所以我愿意回答任何可以解决其中任何一个问题的问题:更新:空传播成员访问计划包含在C#6.0中。不过,我还是更喜欢表达式分解的方案。如果它只是一个简单的成员访问表达式链,那么有一个简单的解决方案:}privatestaticobjectGetValueOfExpression(Ttarget,Expressionexp){if(exp.NodeType==ExpressionType.Parameter){returntarget;}elseif(exp.NodeType==ExpressionType.MemberAccess){varmemberExpression=(MemberExpression)exp;varparentValue=GetValueOfExpression(target,memberExpression.Expression);如果(parentValue==null){返回null;}else{if(memberExpression.MemberisPropertyInfo)return((PropertyInfo)memberExpression.Member).GetValue(parentValue,null);否则返回((FieldInfo)memberExpression.Member).GetValue(parentValue);}}else{thrownewArgumentException("表达式必须只包含成员访问调用。","exp");方法调用支持,请使用这个更新方法:以上是C#学习教程:如何分解成员访问表达式链?如果分享的内容对你有用,需要了解更多C#学习教程,希望大家多多关注~privatestaticobjectGetValueOfExpression(Ttarget,Expressionexp){if(exp==null){返回null;}elseif(exp.NodeType==ExpressionType.Parameter){返回目标;}elseif(exp.NodeType==ExpressionType.Constant){return((ConstantExpression)exp).Value;}elseif(exp.NodeType==ExpressionType.Lambda){returnexp;}elseif(exp.NodeType==ExpressionType.MemberAccess){varmemberExpression=(MemberExpression)exp;varparentValue=GetValueOfExpression(target,memberExpression.Expression);如果(parentValue==null){返回null;}else{if(memberExpression.MemberisPropertyInfo)return((PropertyInfo)memberExpression.Member).GetValue(parentValue,null);否则返回((FieldInfo)memberExpression.Member).GetValue(parentValue);}}elseif(exp.NodeType==ExpressionType.Call){varmethodCallExpression=(MethodCallExpression)exp;varparentValue=GetValueOfExpression(target,methodCallExpression.Object);如果(父值==null&&!methodCallExpression.Method.IsStatic){返回null;}else{vararguments=methodCallExpression.Arguments.Select(a=>GetValueOfExpression(target,a)).ToArray();//将表达式参数转换为委托调用所必需的varparameters=methodCallExpression.Method.GetParameters();for(inti=0;i0&&arguments[0]==null&&methodCallExpression.Method.IsStatic&&methodCallExpression.Method.IsDefined(typeof(ExtensionAttribute),false))//扩展方法{returnnull;}else{returnmethodCallExpression.Method.Invoke(parentValue,arguments);}}}else{thrownewArgumentException(string.Format("表达式类型'{0}'对成员调用无效。",exp.NodeType));}}本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如有转载请注明出处: