好; 相当长的代码段,但这是表达式重写 器的入门指南 ;它尚不能处理少数情况(稍后再解决),但它适用于给定的示例以及 许多 其他示例:
using System;using System.Collections.Generic;using System.Linq;using System.Linq.expressions;using System.Text.Regularexpressions;public class GrandParent{ public Parent Parent { get; set; }}public class Parent{ public Child Child { get; set; } public string Method(string s) { return s + "abc"; }}public class Child{ public string Name { get; set; }}public static class expressionUtils{ public static expression<Func<T1, T3>> Combine<T1, T2, T3>( this expression<Func<T1, T2>> outer, expression<Func<T2, T3>> inner, bool inline) { var invoke = expression.Invoke(inner, outer.Body); expression body = inline ? new expressionRewriter().AutoInline(invoke) : invoke; return expression.Lambda<Func<T1, T3>>(body, outer.Parameters); }}public class expressionRewriter{ internal expression AutoInline(Invocationexpression expression) { isLocked = true; if(expression == null) throw new ArgumentNullException("expression"); Lambdaexpression lambda = (Lambdaexpression)expression.expression; expressionRewriter childScope = new expressionRewriter(this); var lambdaParams = lambda.Parameters; var invokeArgs = expression.Arguments; if (lambdaParams.Count != invokeArgs.Count) throw new InvalidOperationException("Lambda/invoke mismatch"); for(int i = 0 ; i < lambdaParams.Count; i++) { childScope.Subst(lambdaParams[i], invokeArgs[i]); } return childScope.Apply(lambda.Body); } public expressionRewriter() { subst = new Dictionary<expression, expression>(); } private expressionRewriter(expressionRewriter parent) { if (parent == null) throw new ArgumentNullException("parent"); subst = new Dictionary<expression, expression>(parent.subst); inline = parent.inline; } private bool isLocked, inline; private readonly Dictionary<expression, expression> subst; private void CheckLocked() { if(isLocked) throw new InvalidOperationException( "You cannot alter the rewriter after Apply has been called"); } public expressionRewriter Subst(expression from, expression to) { CheckLocked(); subst.Add(from, to); return this; } public expressionRewriter Inline() { CheckLocked(); inline = true; return this; } public expression Apply(expression expression) { isLocked = true; return Walk(expression) ?? expression; } private static IEnumerable<expression> CoalesceTerms( IEnumerable<expression> sourceWithNulls, IEnumerable<expression> replacements) { if(sourceWithNulls != null && replacements != null) { using(var left = sourceWithNulls.GetEnumerator()) using (var right = replacements.GetEnumerator()) { while (left.MoveNext() && right.MoveNext()) { yield return left.Current ?? right.Current; } } } } private expression[] Walk(IEnumerable<expression> expressions) { if(expressions == null) return null; return expressions.Select(expr => Walk(expr)).ToArray(); } private static bool HasValue(expression[] expressions) { return expressions != null && expressions.Any(expr => expr != null); } // returns null if no need to rewrite that branch, otherwise // returns a re-written branch private expression Walk(expression expression) { if (expression == null) return null; expression tmp; if (subst.TryGetValue(expression, out tmp)) return tmp; switch(expression.NodeType) { case expressionType.Constant: case expressionType.Parameter: { return expression; // never a need to rewrite if not already matched } case expressionType.MemberAccess: { Memberexpression me = (Memberexpression)expression; expression target = Walk(me.expression); return target == null ? null : expression.MakeMemberAccess(target, me.Member); } case expressionType.Add: case expressionType.Divide: case expressionType.Multiply: case expressionType.Subtract: case expressionType.AddChecked: case expressionType.MultiplyChecked: case expressionType.SubtractChecked: case expressionType.And: case expressionType.Or: case expressionType.ExclusiveOr: case expressionType.Equal: case expressionType.NotEqual: case expressionType.AndAlso: case expressionType.OrElse: case expressionType.Power: case expressionType.Modulo: case expressionType.GreaterThan: case expressionType.GreaterThanOrEqual: case expressionType.LessThan: case expressionType.LessThanOrEqual: case expressionType.LeftShift: case expressionType.RightShift: case expressionType.Coalesce: case expressionType.ArrayIndex: { Binaryexpression binExp = (Binaryexpression)expression; expression left = Walk(binExp.Left), right = Walk(binExp.Right); return (left == null && right == null) ? null : expression.MakeBinary( binExp.NodeType, left ?? binExp.Left, right ?? binExp.Right, binExp.IsLiftedToNull, binExp.Method, binExp.Conversion); } case expressionType.Not: case expressionType.UnaryPlus: case expressionType.Negate: case expressionType.NegateChecked: case expressionType.Convert: case expressionType.ConvertChecked: case expressionType.TypeAs: case expressionType.ArrayLength: { Unaryexpression unExp = (Unaryexpression)expression; expression operand = Walk(unExp.Operand); return operand == null ? null : expression.MakeUnary(unExp.NodeType, operand, unExp.Type, unExp.Method); } case expressionType.Conditional: { Conditionalexpression ce = (Conditionalexpression)expression; expression test = Walk(ce.Test), ifTrue = Walk(ce.IfTrue), ifFalse = Walk(ce.IfFalse); if (test == null && ifTrue == null && ifFalse == null) return null; return expression.Condition(test ?? ce.Test, ifTrue ?? ce.IfTrue, ifFalse ?? ce.IfFalse); } case expressionType.Call: { MethodCallexpression mce = (MethodCallexpression)expression; expression instance = Walk(mce.Object); expression[] args = Walk(mce.Arguments); if (instance == null && !HasValue(args)) return null; return expression.Call(instance, mce.Method, CoalesceTerms(args, mce.Arguments)); } case expressionType.TypeIs: { TypeBinaryexpression tbe = (TypeBinaryexpression)expression; tmp = Walk(tbe.expression); return tmp == null ? null : expression.TypeIs(tmp, tbe.TypeOperand); } case expressionType.New: { Newexpression ne = (Newexpression)expression; expression[] args = Walk(ne.Arguments); if (HasValue(args)) return null; return ne.Members == null ? expression.New(ne.Constructor, CoalesceTerms(args, ne.Arguments)) : expression.New(ne.Constructor, CoalesceTerms(args, ne.Arguments), ne.Members); } case expressionType.ListInit: { ListInitexpression lie = (ListInitexpression)expression; Newexpression ctor = (Newexpression)Walk(lie.Newexpression); var inits = lie.Initializers.Select(init => new { Original = init, NewArgs = Walk(init.Arguments) }).ToArray(); if (ctor == null && !inits.Any(init => HasValue(init.NewArgs))) return null; ElementInit[] initArr = inits.Select(init => expression.ElementInit( init.Original.AddMethod, CoalesceTerms(init.NewArgs, init.Original.Arguments))).ToArray(); return expression.ListInit(ctor ?? lie.Newexpression, initArr); } case expressionType.NewArrayBounds: case expressionType.NewArrayInit: case expressionType.Invoke: case expressionType.Lambda: case expressionType.MemberInit: case expressionType.Quote: throw new NotImplementedException("Not implemented: " + expression.NodeType); default: throw new NotSupportedException("Not supported: " + expression.NodeType); } }}static class Program{ static void Main() { expression<Func<GrandParent, Parent>> myFirst = gp => gp.Parent; expression<Func<Parent, string>> mySecond = p => p.Child.Name; expression<Func<GrandParent, string>> outputWithInline = myFirst.Combine(mySecond, false); expression<Func<GrandParent, string>> outputWithoutInline = myFirst.Combine(mySecond, true); expression<Func<GrandParent, string>> call = expressionUtils.Combine<GrandParent, Parent, string>( gp => gp.Parent, p => p.Method(p.Child.Name), true); unchecked { expression<Func<double, double>> mathUnchecked = expressionUtils.Combine<double, double, double>(x => (x * x) + x, x => x - (x / x), true); } checked { expression<Func<double, double>> mathChecked = expressionUtils.Combine<double, double, double>(x => x - (x * x) , x => (x / x) + x, true); } expression<Func<int,int>> bitwise = expressionUtils.Combine<int, int, int>(x => (x & 0x01) | 0x03, x => x ^ 0xFF, true); expression<Func<int, bool>> logical = expressionUtils.Combine<int, bool, bool>(x => x == 123, x => x != false, true); expression<Func<int[][], int>> arrayAccess = expressionUtils.Combine<int[][], int[], int>(x => x[0], x => x[0], true); expression<Func<string, bool>> isTest = expressionUtils.Combine<string,object,bool>(s=>s, s=> s is Regex, true); expression<Func<List<int>>> f = () => new List<int>(new int[] { 1, 1, 1 }.Length); expression<Func<string, Regex>> asTest = expressionUtils.Combine<string, object, Regex>(s => s, s => s as Regex, true); var initTest = expressionUtils.Combine<int, int[], List<int>>(i => new[] {i,i,i}, arr => new List<int>(arr.Length), true); var anonAndListTest = expressionUtils.Combine<int, int, List<int>>( i => new { age = i }.age, i => new List<int> {i, i}, true); }}


