/// <summary> /// Determines whether two unaries are similar. /// </summary> /// <remarks> /// Two instances of <see cref="UnaryExpression"/> are similar when they share the same /// <see cref="ExpressionType"/>, method information, and when their operands pass /// equivalency. /// </remarks> /// <param name="source">The source <see cref="UnaryExpression"/>.</param> /// <param name="target">The target <see cref="UnaryExpression"/>.</param> /// <returns>A flag that indicates whether the two expressions are Similar.</returns> private bool UnariesAreSimilar( UnaryExpression source, UnaryExpression target) { if (source.NodeType != target.NodeType) { return(false); } if ((source.Method != null && target.Method == null) || (source.Method == null && target.Method != null)) { return(false); } if (source.Method != null) { if (!ExpressionSimilarity.TypesAreSimilar(source.Method.DeclaringType, target.Method.DeclaringType) || source.Method.Name != target.Method.Name) { return(false); } } return(ExpressionSimilarity.IsPartOf(source.Operand, target.Operand)); }
/// <summary> /// Check for equivalent array initializers. /// </summary> /// <remarks> /// To be true, type must be equivalent and expressions must be similar. /// </remarks> /// <param name="source">The source <see cref="NewArrayExpression"/>.</param> /// <param name="target">The target <see cref="NewArrayExpression"/>.</param> /// <returns>A flag indicating whether the array initializers are equivalent.</returns> private bool NewArraysAreSimilar( NewArrayExpression source, NewArrayExpression target) { if (source.Type != target.Type) { return(false); } return(ExpressionSimilarity.AreSimilar(source.Expressions, target.Expressions)); }
/// <summary> /// Determines whether two binaries are similar. /// </summary> /// <remarks> /// Two instances of <see cref="BinaryExpression"/> are similar when they share the same /// <see cref="ExpressionType"/> and the recursive determination of the left expressoin and /// the right expressions is similar. /// </remarks> /// <param name="source">The source <see cref="BinaryExpression"/>.</param> /// <param name="target">The target <see cref="BinaryExpression"/>.</param> /// <returns>A flag that indicates whether the two expressions are similar.</returns> private bool BinariesAreSimilar( BinaryExpression source, BinaryExpression target) { if (source.NodeType != target.NodeType) { return(false); } return(ExpressionSimilarity.IsPartOf(source.Left, target.Left) && ExpressionSimilarity.IsPartOf(source.Right, target.Right)); }
/// <summary> /// Determine whether two members are similar. /// </summary> /// <remarks> /// Two instances of <see cref="MemberExpression"/> are similar /// when they share the same type (this will match the member type), the same /// declaring type, the same name, and if there is an expression, the /// expressions are similar. /// </remarks> /// <param name="source">The source <see cref="MemberExpression"/>.</param> /// <param name="target">The target <see cref="MemberExpression"/>.</param> /// <returns>A flag indicating whether the two expressions are Similar.</returns> private bool MembersAreSimilar( MemberExpression source, MemberExpression target) { if (!ExpressionSimilarity.TypesAreSimilar(source.Type, target.Type) || !ExpressionSimilarity.TypesAreSimilar(source.Member.DeclaringType, target.Member.DeclaringType) || source.Member.Name != target.Member.Name) { return(false); } return(source.Expression == null || ExpressionSimilarity.IsPartOf(source.Expression, target.Expression)); }
/// <summary> /// Method to compare two <seealso cref="ConstantExpression"/> /// instances. /// </summary> /// <remarks> /// If the constant is an expression, similarity is recursed. If it is a value type, /// the source and target must be equal. Otherwise, similar is based on types. /// </remarks> /// <param name="source">The source <see cref="ConstantExpression"/>.</param> /// <param name="target">The target <see cref="ConstantExpression"/>.</param> /// <returns>A flag indicating whether the two are similar.</returns> private bool ConstantsAreSimilar( ConstantExpression source, ConstantExpression target) { if (!ExpressionSimilarity.TypesAreSimilar(source.Type, typeof(Expression)) && !ExpressionSimilarity.TypesAreSimilar(source.Type, target.Type)) { return(false); } if (source.Value == null) { return(target.Value == null); } if (source.Value is Expression expression) { return(ExpressionSimilarity.AreSimilar( expression, target.Value as Expression)); } // lists if (typeof(Array).IsAssignableFrom(source.Type) || typeof(System.Collections.ICollection).IsAssignableFrom(source.Type)) { return(true); } // string if (source.Type == typeof(string)) { return(source.Value == target.Value); } // typed collections if (source.Type.GetInterfaces() .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable <>))) { return(true); } // "primitives" return(eq.ValuesAreEquivalent( source.Value, target.Value)); }
/// <summary> /// Check for similar member initialization. /// </summary> /// <remarks> /// The expression must be similar, the types similar, and every binding must be equivalent. /// </remarks> /// <param name="source">The source <see cref="MemberInitExpression"/>.</param> /// <param name="target">The target <see cref="MemberInitExpression"/>.</param> /// <returns>A flag indicating whether the member initializations are similar.</returns> private bool MemberInitAreSimilar( MemberInitExpression source, MemberInitExpression target) { if (!ExpressionSimilarity.TypesAreSimilar(source.Type, target.Type)) { return(false); } if (!ExpressionSimilarity.AreSimilar(source.NewExpression, target.NewExpression)) { return(false); } return(ExpressionSimilarity.MemberBindingsAreSimilar(source.Bindings, target.Bindings)); }
/// <summary> /// Determine whether two methods are similar. /// </summary> /// <remarks> /// Two metods are similar when they share the same return type, declaring type, /// the same name, are either both instance or static fields, and all /// arguments pass similarity. Arguments of same expression type are tested /// for similarity. Arguments of different expression types are tested for the /// same return type. /// </remarks> /// <param name="source">The source <see cref="MethodCallExpression"/>.</param> /// <param name="target">The target <see cref="MethodCallExpression"/>.</param> /// <returns>A flag indicating whether the two expressions are Similar.</returns> private bool MethodsAreSimilar( MethodCallExpression source, MethodCallExpression target) { if (!ExpressionSimilarity.TypesAreSimilar(source.Type, target.Type) || !ExpressionSimilarity.TypesAreSimilar(source.Method.DeclaringType, target.Method.DeclaringType) || source.Method.Name != target.Method.Name) { return(false); } if (source.Arguments.Count != target.Arguments.Count) { return(false); } // always null when static if (source.Object != null) { if (!ExpressionSimilarity.IsPartOf(source.Object, target.Object)) { return(false); } } for (var idx = 0; idx < source.Arguments.Count; idx += 1) { bool similar; if (source.Arguments[idx].GetType() == target.Arguments[idx].GetType()) { similar = ExpressionSimilarity.IsPartOf(source.Arguments[idx], target.Arguments[idx]); } else { similar = ExpressionSimilarity.TypesAreSimilar(source.Type, target.Type); } if (!similar) { return(false); } } return(true); }
/// <summary> /// Check for similar object initializer. /// </summary> /// <remarks> /// To be true, type must be similar, /// parameters are similar and arguments are similar. /// </remarks> /// <param name="source">The source <see cref="NewExpression"/>.</param> /// <param name="target">The target <see cref="NewExpression"/>.</param> /// <returns>A flag indicating whether the object initializers are equivalent.</returns> private bool NewAreSimilar( NewExpression source, NewExpression target) { if (!ExpressionSimilarity.TypesAreSimilar(source.Type, target.Type)) { return(false); } if (!ExpressionSimilarity.ParameterInfosAreSimilar( source.Constructor.GetParameters(), target.Constructor.GetParameters())) { return(false); } return(ExpressionSimilarity.AreSimilar(source.Arguments, target.Arguments)); }
/// <summary> /// Method to compare two <see cref="InvocationExpression"/> for similarty. /// </summary> /// <remarks> /// To be similar, types must be assignable, arguments must be similar, /// and the source expression must be part of the target expression. /// </remarks> /// <param name="source">The source <see cref="InvocationExpression"/>.</param> /// <param name="target">The target <see cref="InvocationExpression"/>.</param> /// <returns>A value that indicates whether they are similar.</returns> private bool InvocationsAreSimilar( InvocationExpression source, InvocationExpression target) { if (!ExpressionSimilarity.TypesAreSimilar(source.Type, target.Type)) { return(false); } if (source.Arguments.Count != target.Arguments.Count) { return(false); } if (!ExpressionSimilarity.AreSimilar(source.Arguments, target.Arguments)) { return(false); } return(ExpressionSimilarity.IsPartOf(source.Expression, target.Expression)); }
/// <summary> /// Determine whether two lambdas are similar. /// </summary> /// <remarks> /// Two lambda expressions are similar when they share the same name, /// similar parameters, and when both body and parameters are similar. /// </remarks> /// <param name="source">The source <see cref="LambdaExpression"/>.</param> /// <param name="target">The target <see cref="LambdaExpression"/>.</param> /// <returns>A flag indicating whether the two expressions are similar.</returns> private bool LambdasAreSimilar( LambdaExpression source, LambdaExpression target) { if (source.Name != target.Name) { return(false); } if (source.Parameters.Count != target.Parameters.Count) { return(false); } if (!ExpressionSimilarity.AreSimilar(source.Parameters, target.Parameters)) { return(false); } return(ExpressionSimilarity.IsPartOf(source.Body, target.Body)); }
/// <summary> /// Initializes a new instance of the <see cref="DefaultHighPerformanceRules"/> class. /// </summary> public DefaultHighPerformanceRules() { equivalencyRules = new Dictionary <Type, Func <Expression, Expression, bool> > { { typeof(LambdaExpression), (s, t) => ExecuteRuleAs <LambdaExpression>(s, t, LambdasAreEquivalent) }, { typeof(ConstantExpression), (s, t) => ExecuteRuleAs <ConstantExpression>(s, t, ConstantsAreEquivalent) }, { typeof(InvocationExpression), (s, t) => ExecuteRuleAs <InvocationExpression>(s, t, InvocationsAreEquivalent) }, { typeof(MemberExpression), (s, t) => ExecuteRuleAs <MemberExpression>(s, t, MembersAreEquivalent) }, { typeof(MethodCallExpression), (s, t) => ExecuteRuleAs <MethodCallExpression>(s, t, MethodsAreEquivalent) }, { typeof(BinaryExpression), (s, t) => ExecuteRuleAs <BinaryExpression>(s, t, BinariesAreEquivalent) }, { typeof(NewArrayExpression), (s, t) => ExecuteRuleAs <NewArrayExpression>(s, t, NewArraysAreEquivalent) }, { typeof(NewExpression), (s, t) => ExecuteRuleAs <NewExpression>(s, t, NewAreEquivalent) }, { typeof(ParameterExpression), (s, t) => ExecuteRuleAs <ParameterExpression>(s, t, ParametersAreEquivalent) }, { typeof(UnaryExpression), (s, t) => ExecuteRuleAs <UnaryExpression>(s, t, UnariesAreEquivalent) }, { typeof(MemberInitExpression), (s, t) => ExecuteRuleAs <MemberInitExpression>(s, t, MemberInitAreEquivalent) }, }; similarityRules = new Dictionary <Type, Func <Expression, Expression, bool> > { { typeof(LambdaExpression), (s, t) => ExecuteRuleAs <LambdaExpression>(s, t, LambdasAreSimilar) }, { typeof(ConstantExpression), (s, t) => ExecuteRuleAs <ConstantExpression>(s, t, ConstantsAreSimilar) }, { typeof(MemberExpression), (s, t) => ExecuteRuleAs <MemberExpression>(s, t, MembersAreSimilar) }, { typeof(InvocationExpression), (s, t) => ExecuteRuleAs <InvocationExpression>(s, t, InvocationsAreSimilar) }, { typeof(MethodCallExpression), (s, t) => ExecuteRuleAs <MethodCallExpression>(s, t, MethodsAreSimilar) }, { typeof(BinaryExpression), (s, t) => ExecuteRuleAs <BinaryExpression>(s, t, BinariesAreSimilar) }, { typeof(NewArrayExpression), (s, t) => ExecuteRuleAs <NewArrayExpression>(s, t, NewArraysAreSimilar) }, { typeof(NewExpression), (s, t) => ExecuteRuleAs <NewExpression>(s, t, NewAreSimilar) }, { typeof(ParameterExpression), (s, t) => ExecuteRuleAs <ParameterExpression>( s, t, (src, tgt) => ExpressionSimilarity.TypesAreSimilar(src.Type, tgt.Type)) }, { typeof(UnaryExpression), (s, t) => ExecuteRuleAs <UnaryExpression>(s, t, UnariesAreSimilar) }, { typeof(MemberInitExpression), (s, t) => ExecuteRuleAs <MemberInitExpression>(s, t, MemberInitAreSimilar) }, }; }
/// <summary> /// Determines whether an <see cref="IQueryable{T}"/> is part of another query. /// </summary> /// <remarks> /// A source is part of a target if an <see cref="Expression"/> exists in the /// target's tree that is similar to the source. /// </remarks> /// <typeparam name="T">The <see cref="Type"/> of the entity.</typeparam> /// <param name="source">The source <see cref="IQueryable{T}"/>.</param> /// <param name="target">The target <see cref="IQueryable{T}"/> to parse.</param> /// <returns>A flag indicating whether the source is part of the target.</returns> public bool IsPartOf <T>(IQueryable <T> source, IQueryable <T> target) => ExpressionSimilarity.IsPartOf(source?.Expression, target?.Expression);
/// <summary> /// Determines whether an <see cref="Expression"/> is part of another expression. /// </summary> /// <remarks> /// A source is part of a target if an <see cref="Expression"/> exists in the /// target's tree that is similar to the source. /// </remarks> /// <param name="source">The source <see cref="Expression"/>.</param> /// <param name="target">The target <see cref="Expression"/> to parse.</param> /// <returns>A flag indicating whether the source is part of the target.</returns> public bool IsPartOf(Expression source, Expression target) => ExpressionSimilarity.IsPartOf(source, target);
/// <summary> /// Comparison of multiple expressions. Similar /// only when all elements match, in order, and /// pass the similarity test. It's fine if the /// source does not have the same number of entities /// as the target. /// </summary> /// <param name="source">The source expressions.</param> /// <param name="target">The target expressions.</param> /// <returns>A flag indicating whether the two sets of /// expressions are Similar.</returns> public bool AreSimilar( IEnumerable <Expression> source, IEnumerable <Expression> target) => ExpressionSimilarity.AreSimilar(source, target);
/// <summary> /// Entry for similarity comparisons. Will cast to /// known types and compare. /// </summary> /// <typeparam name="T">The <see cref="Type"/> of entity.</typeparam> /// <param name="source">The source <see cref="IQueryable{T}"/>.</param> /// <param name="target">The target <see cref="IQueryable{T}"/> to compare to.</param> /// <returns>A flag indicating whether the source and target are similar.</returns> public bool AreSimilar <T>(IQueryable <T> source, IQueryable <T> target) => ExpressionSimilarity.AreSimilar(source?.Expression, target?.Expression);
/// <summary> /// Entry for similarity comparisons. Will cast to /// known types and compare. /// </summary> /// <param name="source">The source <see cref="Expression"/>.</param> /// <param name="target">The target <see cref="Expression"/> to compare to.</param> /// <returns>A flag indicating whether the source and target are similar.</returns> public bool AreSimilar(Expression source, Expression target) => ExpressionSimilarity.AreSimilar(source, target);