3

Somewhat similar question here:

Difference between C# and VB.Net string comparison

...but not the same as the one I am asking now.

I am creating a simple expression walker that will convert a lambda into an SQL WHERE clause. I call it like this:

GetEntities<MyEntity>(e => e.MyProperty == MyValue)

C# creates the expression as I would expect which is a BinaryExpression consisting of a MemberExpression on the left and a ConstantExpression on the right which looks like this:

$e.MyProperty == MyValue

VB.NET, however, calls CompareString, to which it passes MyProperty and MyValue as parameters and then checks the return result for 0. When called like this:

GetEntities(Of MyEntity)(Function(e) e.MyProperty = MyValue)

...it generates an expression like this:

.Call Microsoft.VisualBasic.CompilerServices.Operators.CompareString(
    $e.MyProperty, MyValue, False) == 0

This obviously doesn't play too well with my expression walker, so I will have to now walk the method expression to get the value passed into it and blah blah blah.

Is there a way to force VB.NET to generate the same expression tree as C# in all circumstances? I'd hate to have to write a ton of code to account for these significant differences.

Community
  • 1
  • 1
oscilatingcretin
  • 10,457
  • 39
  • 119
  • 206
  • Can you show your VB code as well for completeness – Matt Wilko Jan 26 '16 at 13:45
  • 3
    The reason it creates a different tree is that `=` in VB is not the same as `==` in C# (e.g. null handling and case sensitivity). Do you want your walker to use the C# behaviour i VB? Wouldn't that confuse a VB user? – adrianm Jan 26 '16 at 13:58
  • Try e.MyProperty.Equals(MyValue) – Aaron Jan 26 '16 at 14:03
  • @adrianm How would anyone be confused? The semantics between `a == b` are identical to `a = b` in the context of equality checking. All of this expression work is done behind the scenes, so the developer only needs to understand that they're doing an equality check. – oscilatingcretin Jan 26 '16 at 14:14
  • 1
    @oscilatingcretin But the semantics *aren't* the same. You just don't care about the differences. That doesn't mean that there aren't any. – Servy Jan 26 '16 at 14:15
  • @Servy How are the semantic differences between `==` and `=` anything other than syntactical? I understand that VB.NET has to account for `option compare binary` for compatibility with code that has been converted from oldschool VB, but, so long as binary compare is on, `=` is doing the same thing as `==`, is it not? – oscilatingcretin Jan 26 '16 at 14:20
  • @oscilatingcretin Relevant differences [have already been mentioned](http://stackoverflow.com/questions/35015198/force-vb-net-to-generate-the-same-string-comparison-expression-as-c?noredirect=1#comment57757710_35015198). – Servy Jan 26 '16 at 14:22
  • @oscilatingcretin, in Vb.Net `"" = Nothing` and case sensitivity depends on `Option Compare` – adrianm Jan 26 '16 at 14:28
  • @adrianm Okay, now I see that you are correct. My misunderstanding was that I assumed with `option strict on`, VB.NET would not look at `""` as `Nothing`. It turns out that it does indeed which is unfortunate. – oscilatingcretin Jan 26 '16 at 14:38

1 Answers1

2

As I noted in a comment there is a reason for the difference. The Vb.Net operators does not work in the same way as in C#.

So the answer to your questions is no, you can't change Vb.Net's operators to work like C#.

What you can do is to create a transform from Vb expression tree to C# expression tree:

    internal sealed class VbComparisonTransform : ExpressionVisitor
    {
        protected override Expression VisitBinary(BinaryExpression node) {
            if (node == null)
                throw new ArgumentNullException("node");

            if (node.Left.NodeType != ExpressionType.Call)
                return base.VisitBinary(node);

            var callNode = node.Left as MethodCallExpression;
            if (callNode.Method.DeclaringType.FullName != "Microsoft.VisualBasic.CompilerServices.Operators")
                return base.VisitBinary(node);
            if (callNode.Method.Name != "CompareString")
                return base.VisitBinary(node);

            switch (node.NodeType) {
                case ExpressionType.LessThan:
                    return Expression.LessThan(callNode.Arguments[0], callNode.Arguments[1]);
                case ExpressionType.LessThanOrEqual:
                    return Expression.LessThanOrEqual(callNode.Arguments[0], callNode.Arguments[1]);
                case ExpressionType.Equal:
                    return Expression.Equal(callNode.Arguments[0], callNode.Arguments[1]);
                case ExpressionType.NotEqual:
                    return Expression.NotEqual(callNode.Arguments[0], callNode.Arguments[1]);
                case ExpressionType.GreaterThanOrEqual:
                    return Expression.GreaterThanOrEqual(callNode.Arguments[0], callNode.Arguments[1]);
                case ExpressionType.GreaterThan:
                    return Expression.GreaterThan(callNode.Arguments[0], callNode.Arguments[1]);
                default:
                    string throwmessage = string.Format(CultureInfo.InvariantCulture, "VB.Net compare expression of type {0} not supported", node.NodeType);
                    throw new NotSupportedException(throwmessage);
            }
        }
    }

and then use it like this:

public Expression ToCSharpComparisons(Expression expression) {
    if (expression == null)
        throw new ArgumentNullException("expression");

    return new VbComparisonTransform().Visit(expression);
}
adrianm
  • 14,468
  • 5
  • 55
  • 102