49

I have a library working on expression trees. The library need to work with both C# and VB.Net

Noticed some differences between the languages on how the expression trees are constructed

  • String comparison
    () => "a" == "b" becomes Expression.Equals("a", "b")
    Function() "a" = "b" becomes Expression.Equals(Expression.Call(CompareString, "a", "b"), 0)
    (I understand why VB.Net uses CompareString here)

  • String concatenation
    () => "a" + "b" becomes Expression.Add("a", "b", String.Concat)
    Function() "a" & "b" becomes Expression.Call(String.Concat, "a", "b")

  • Optimization?
    () => !(1 == 2) becomes Expression.Not(Expression.Equals(1, 2))
    Function() Not (1 = 2) becomes Expression.NotEqual(1, 2)

My library handle all these differences but are there more differences I need to look out for?

Edit Some explanation of what my code does.

The system I work with has a filter for documents that you specify like this:

var filter = document.Filter;
filter.LeftParanthesis();
filter.Column(columnNumber);
filter.Equals();
filter.Value("abc");
filter.RightParanthesis();
filter.And();
filter.LeftParanthesis();
...
document.Refresh();

To make it easier to use the filter my code allows you to specify the filter as a Expression<Func<bool>> lambda.

Expression<Func<bool>> filter = () => (123.AsStringColumn() == "abc") && (...);
filter.Apply(document);

My code then iterates the expression tree and calls the document filter methods as specified above. The filter does not have support for everything you can put into a lambda. Method calls is the most obvious one.

Since VB.Net generates method calls in some cases where C# does not I need to intercept these and handle them differently.

adrianm
  • 14,468
  • 5
  • 55
  • 102
  • 21
    captured-variables, anonymous-types, and opaque-identifiers (`let`) probably need some love; I suspect this is open-ended, though – Marc Gravell Apr 19 '13 at 11:31
  • @Marc The naming is probably different but do you expect the tree to look different as well? – adrianm Apr 19 '13 at 12:12
  • 3
    Look out for implicit conversions with `option strict off` in VB.NET. Not really a difference per se since C# doesn't support it, but it might introduce extra calls you might be interested in handling. – Julien Lebosquain Apr 19 '13 at 12:42
  • @Julien Thanks for mentioning conversions. Fortunatly I don't need to handle them since the target system does not support conversions and I already support constant conversions via constant sub-expression evaluation. – adrianm Apr 19 '13 at 14:27
  • What about non-standard object comparisons (user written comparison routines)? – Adam Zuckerman Apr 21 '13 at 08:42
  • @adam Do you have an example where the expression tree for non standard comparisons is different depending on if it is generated in C# or Vb.Net? I mentioned how string comparison differs in the question but have not seen it for other types. – adrianm Apr 21 '13 at 13:15
  • I do not. I have seen examples on MSDN. Check out http://support.microsoft.com/kb/320727 – Adam Zuckerman Apr 21 '13 at 22:08
  • 1
    Please remember that from .NET version to .NET version these little differences might get changed. – Martin Mulder Apr 27 '13 at 11:28
  • 1
    Have you seen this? One more difference: http://stackoverflow.com/questions/6811868/expression-trees-different-compiler-behaviour-for-actiont-in-c-sharp-and-vb – Kamil Apr 27 '13 at 16:31
  • @kamil, looked at the link and did some tests. The difference is what operations VB.Net allows in expressions. The extra operations allowed does not make sense in my context so it is ok to get an error message. Thanks for the link. – adrianm Apr 30 '13 at 07:39
  • Sorry for the dumb question, but what exactly are you doing with these expression trees. My understanding is that if you were building these trees manually, either approach would be valid, in either language - ie the results would be the same. What, specifically, is the problem that arises from these differences? – guysherman May 02 '13 at 00:46
  • @guysherman, I work with an ERP system where you build filters using methods like `.ColumnEqual(columnName, value)` etc. My code convert an expression tree to the ERP methods similar to how Linq2Sql converts to a WHERE clause. – adrianm May 02 '13 at 07:12
  • 4
    We use a C# library that generates and mutates expression trees and is used heavily by VB users as well as C# users. We have NO special code to handle any differences so I'm not sure there is a real issue here. You don't need to write the lib in both languages, you just need to consume it in both languages. – Jay Traband May 02 '13 at 09:38
  • 1
    "are there more differences I need to look out for? ... The filter does not have support for everything you can put into a lambda" - it seems to me you simply need to enumerate in your test cases all the C# and VB.NET expressions you *do* support, and ensure they are handled by your code. You're happy to say that not everything is supported, so decide what *is* supported, and support it. – AakashM May 02 '13 at 11:41
  • I know that product, and I think you're going to run into other problems. For example, if you have parentheses around a single condition, it tends to display a "SQL Query Error" message to the user. – Richard Deeming May 02 '13 at 14:21
  • @AakashM, that is what I do but getting full coverage for combinations of 20+ nodes with 3 different types in two languages is a lot of work. Richard, it works fine. I use it daily but I only use c#. The rules are not that difficult and using expression trees hides the details from the user. – adrianm May 02 '13 at 17:15
  • 3
    This looks similar to my question I asked a few years ago. http://stackoverflow.com/questions/6742972/why-do-lambda-expressions-in-vb-differ-from-c – Phill May 04 '13 at 18:14
  • I know the compilers handles differently c# `"a"=="b"` and vb.net `"a"="b"` boolean expression. So some of the differences might reflect differences in compilers. – John Alexiou May 04 '13 at 22:24
  • 2
    Instead, can you look into Dynamic LINQ? Even if VB generates little different lambda, why dont you just compile and invoke lambda, why do you have to intercept them? You dont need to do anything with lambda if you implement IList or ICollection or IEnumerable properly, Linq will do its job regardless. – Akash Kava May 05 '13 at 10:57
  • The question by itself is very interesting.. but what you are trying to do is a nightmare from a maintenance point of view. It will change from C# version to the next one, and in the next version of VB as well... Until, with Roslyn or later, the two will converge. But now you will have to retest everything every time – Lorenzo Dematté May 10 '13 at 08:26
  • @AkashKava is right. You should implement an iteration strategy for the document using the decorator pattern rather than traversing the expression tree yourself. – Joe Coder May 11 '13 at 06:03
  • @AkashKava, i am not using the expression trees for LINQ. – adrianm May 11 '13 at 21:00
  • @dema80, i don't think the implementation of the trees will change. Changes would break LINQ, EF and any other IQuryable provider. – adrianm May 11 '13 at 21:05
  • @JoeCoder, can you explain what you mean? I am transforming an expression tree into a sequence of method calls. How can I do that without iterating the tree? – adrianm May 11 '13 at 21:13
  • @adrianm I began to implement an answer, but realized that you aren't really doing functional programming here. You're just using expression tree traversal to perform code translation. This is the wrong tool for the job and won't be maintainable as others have mentioned, even if you only implement in one language. – Joe Coder May 12 '13 at 05:24
  • @joecoder, which tool is wrong? What do you mean with implementing in one language? Don't understand the maintainability issue. There are a lot of iqueryable providers like linq2sql and entity framework using the same technique. They will all break if the c# or vb.net compliers starts generating different expression trees. – adrianm May 12 '13 at 09:30
  • @adrianm ...umm.. why? Anyway, that is a good point: find a IQueryable provider that works with both VB.Net and C#, and mimic what it is done there – Lorenzo Dematté May 13 '13 at 07:38
  • @dema80, I have looked at other providers but most seem to not handle vb.net. In Linq2sql I found a few vb specific things explicitly mentioned: string comparison, "like" handling and type conversions. – adrianm May 14 '13 at 06:26
  • Not just between compilers, framework differences itself sometime matter. See this answer for an example and the comments under it: http://stackoverflow.com/a/8228144/661933 – nawfal Dec 13 '13 at 07:27
  • How do you see the *what it becomes* part? Please enlighten the unlettered but curious. :-) I tried this but I get the source code string back: `Expression> predicate = (a, b) => a != b; Console.WriteLine(predicate.ToString());` – Water Cooler v2 Jan 21 '14 at 06:24
  • @WaterCoolerv2, Use linqpad with `predicate.Dump()` – adrianm Jan 21 '14 at 08:14

2 Answers2

6

The / division operator works differently in C# and VB. In C# it adapts to the data types used, while VB always converts the operands to floating point values:

() => 1 / 2 becomes Expression.Divide(1, 2)
Function() 1 / 2 becomes Expression.Divide(Expression.Convert(1, Double), Expression.Convert(2, Double))

In VB you would need to use the \ operator for integer division and / for floating point division to get the same as the / operator in C#.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
2

I had to work with C# and VB.Net frequently related to my project and most of the differences I had seen when VB.Net is in unsafe mode. ie if we make the VB.Net typesafe (Option strict on, option explicite on...option all on) it will work like C#.

Joy George Kunjikkuru
  • 1,495
  • 13
  • 27