hits counter

C# And Visual Basic Generate Different Expression Trees

(This was pointed out to me by Frans Bouma and explained by Jon Skeet)

Imagine you have this set of classes:

public class A
{
    public virtual string P
    {
        get { return "A"; }
    }
}

public class B : A { }

public class C : B { public override string P { get { return "C"; } } }

And this class:

public static class Reporter
{
    public static void Report<T>(T target, Expression<Func<T, string>> expression)
    {
        Console.WriteLine("Expression: {0}", expression);
        Console.WriteLine("\tDeclaring Type: {0}", ((expression as LambdaExpression).Body as MemberExpression).Member.DeclaringType);
        Console.WriteLine("\tInvocation Result: {0} for {1}", expression.Compile().Invoke(target), target.GetType());
        Console.WriteLine();
    }
}

The above class writes to the console the lambda expression, the declaring type of the property and the value of the property for the target and the target’s type.

Now, let’s look at what happens when used from this C# code:

Reporter.Report(new C(), (A a) => a.P);
Reporter.Report(new C(), (B b) => b.P);
Reporter.Report(new C(), (C c) => c.P);

Reporter.Report(new B(), (A a) => a.P); Reporter.Report(new B(), (B b) => b.P);

Reporter.Report(new A(), (A a) => a.P);

The output will be:

Expression: a => a.P
        Declaring Type: A
        Invocation Result: C for C

Expression: b => b.P Declaring Type: A Invocation Result: C for C

Expression: c => c.P Declaring Type: A Invocation Result: C for C

Expression: a => a.P Declaring Type: A Invocation Result: A for B

Expression: b => b.P Declaring Type: A Invocation Result: A for B

Expression: a => a.P Declaring Type: A Invocation Result: A for A

On the other hand, if used from the equivalent Visual Basic code:

Reporter.Report(New C(), Function(a As A) a.P)
Reporter.Report(New C(), Function(b As B) b.P)
Reporter.Report(New C(), Function(c As C) c.P)

Reporter.Report(New B(), Function(a As A) a.P) Reporter.Report(New B(), Function(b As B) b.P)

Reporter.Report(New A(), Function(a As A) a.P)

The output will be:

Expression: a => a.P
        Declaring Type: A
        Invocation Result: C for C

Expression: b => b.P Declaring Type: A Invocation Result: C for C

Expression: c => c.P Declaring Type: C Invocation Result: C for C

Expression: a => a.P Declaring Type: A Invocation Result: A for B

Expression: b => b.P Declaring Type: A Invocation Result: A for B

Expression: a => a.P Declaring Type: A Invocation Result: A for A

Why the differences? It’s because of each language’s specification and compiler:

  • The C# compiler will issue a virtual call to the virtual member in the class where it’s declared as virtual.
  • The Visual Basic compiler will issue a virtual call to the overriding member on the class that overrides it.

No Comments