The CallExpression.Method method always returns the root base class of MethodInfo

Here is a simple application that prints the signature of the MethodCallExpression method:

 using System; using System.Linq; using System.Linq.Expressions; class A { public virtual void Foo() { } } class B : A { public override void Foo() { } } class C : B { public override void Foo() { } } class Program { static void Main(string[] args) { PrintMethod<A>(a => a.Foo()); PrintMethod<B>(b => b.Foo()); PrintMethod<C>(c => c.Foo()); Console.Read(); } static void PrintMethod<T>(Expression<Action<T>> expression) { var body = (MethodCallExpression)expression.Body; var method1 = body.Method; var method2 = typeof(T).GetMethod(body.Method.Name, body.Method.GetParameters().Select(p => p.ParameterType).ToArray()); Console.WriteLine("body.Method -> " + method1.DeclaringType.ToString() + " - " + method1.ToString()); Console.WriteLine("typeof(T).GetMethod -> " + method2.DeclaringType.ToString() + " - " + method2.ToString()); } } 

I expect the program to be printed:

 body.Method -> A - Void Foo() typeof(T).GetMethod -> A - Void Foo() body.Method -> B - Void Foo() * typeof(T).GetMethod -> B - Void Foo() body.Method -> C - Void Foo() * typeof(T).GetMethod -> C - Void Foo() 

But instead, it produces:

 body.Method -> A - Void Foo() typeof(T).GetMethod -> A - Void Foo() body.Method -> A - Void Foo() * typeof(T).GetMethod -> B - Void Foo() body.Method -> A - Void Foo() * typeof(T).GetMethod -> C - Void Foo() 

When the Method property is MethodCallExpression for the inherited MethodCallExpression it always returns A MethodInfo (root class).

However, in Visual Studio and the “Go To Definition” of each of the calls to Foo() I take on each of the overridden methods as expected.

Why MethodCallExpression.Method behave like this? Is there anything in the spec about this? Why is there a mismatch between VS and the Method property? I tested with .NET 4.0 and 4.5.

+6
source share
1 answer

Suppose you have one library:

 public class A { public virtual void Foo() { } } public class B : A { public override void Foo() { } } public class C : B { public override void Foo() { } } 

And you have one consumer who makes

 new C().Foo(); 

You are now updating the library so that C no longer overrides Foo :

 public class C : B { } 

Will there be a need to recompile the consumer?

If the consumer calls C.Foo virtually, then yes, and the consumer will have to write ((A)new C()).Foo() to avoid this problem. If the consumer calls A.Foo virtually, then no. Since this is the only difference, since exactly the same function will be called at runtime, it makes no sense for the user to indicate that he is calling C.Foo .

Expression trees record the same method information as a regular function call. The C # specification says very little about this, it leaves it specific to the implementation (however, the Microsoft implementation does not seem to define (document) it:)

Converting an anonymous function to an expression tree type creates an expression tree (§4.6). More precisely, the evaluation of the transformation of an anonymous function leads to the construction of the structure of the object, which is the structure of the anonymous function itself. The exact structure of the expression tree, as well as the exact process of its creation are determined by the implementation.

+4
source

Source: https://habr.com/ru/post/944441/


All Articles