ExpressionEqualityComparer and Parameter Names
I forked ExpressionEqualityComparer and slightly modified it because my definition of equality of two expressions must disregard differing parameter names.
I am wondering if anyone else has ever had the need to compare expressions without considering parameter (and variable) names.
For example, the change I made would cause these two expressions to be considered as being equal:
Expression<Func<int,int>> a = foo => foo + 10;
Expression<Func<int,int>> a = bar => bar + 10;
The mod also excludes parameter names from hashcode calculation. The change was fairly trivial:
Before:
private bool CompareParameter(ParameterExpression a, ParameterExpression b)
=> _parameterScope != null
&& _parameterScope.TryGetValue(a, out var mapped)
? mapped.Name == b.Name
: a.Name == b.Name;
After:
private bool CompareParameter(ParameterExpression a, ParameterExpression b)
=> true;
Hashcode computation:
Before
case ParameterExpression parameterExpression:
AddToHashIfNotNull(parameterExpression.Name);
break;
After
case ParameterExpression parameterExpression:
break;
So, I guess what I'm getting at here is to propose that ignoring parameter names be a parameterized option.
IIUC you are suggesting that expressions should be considered equal modulo α-conversion.
I agree on that, but I should warn you that the updated CompareParameter would lead to considering equal even expressions that should probably be considered different, for example:
Expression<Func<int,int,int>> twice_x_plus_y = (x, y) => x + x + y;
Expression<Func<int,int,int>> x_plus_twice_y = (x, y) => x + y + y;
EDIT: updated the expressions so that all of the lambda parameters are used in the body to make it more obvious that the issue is the name confusion and not the param (non-)usage.
Related:
- #30755
Thanks for your insight and advice. I'm now thinking about replacing the parameters in one expression with the parameters from the other expression, as one would have to do when combining the expression bodies, and then allowing them to be compared. I'm not sure what other ways there are to achieve this, but what I do know, is that for my purposes, the parameter names are inconsequential, as two otherwise-identical expressions are functionally-equivalent.
I was partially mistaken about the issues. With the existing code, two expressions that use different parameter names but are otherwise identical do compare as equal. The problem was that I was using expressions as Dictionary keys and also storing them in HashSets, and because the existing code uses parameter names as ingredients in hashcode generation, it resulted in different hashcodes for expressions that compare as equal (when they use different parameter names).
The change to the hashcode generation code that I showed above is all that's needed, and the code that suppresses the comparison of parameter names isn't needed because the original code already deals with that correctly.