c# - MethodCallExpression.Method 总是返回根基类的 MethodInfo

标签 c# .net expression-trees

这是一个打印MethodCallExpression 方法签名的简单应用程序:

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());
    }
}

我希望程序打印出来:

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()

但它却打印出:

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()

当获取继承的 MethodCallExpressionMethod 属性时,它总是返回 AMethodInfo(根类)。

但是,在 Visual Studio 中,我在每个 Foo() 调用中“转到定义”,按预期转到每个被覆盖的方法。

为什么 MethodCallExpression.Method 会这样?规范中有关于此的内容吗?为什么 VS 和 Method 属性之间存在差异?我已经使用 .NET 4.0 和 4.5 进行了测试。

最佳答案

假设你有一个图书馆:

public class A
{
    public virtual void Foo() { }
}

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

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

你有一个消费者这样做

new C().Foo();

现在更新库,使 C 不再覆盖 Foo:

public class C : B
{
}

消费者是否需要重新编译?

如果消费者虚拟调用 C.Foo,那么是的,消费者必须专门编写 ((A)new C()).Foo()以避免那个问题。如果消费者虚拟调用 A.Foo,则不会。由于这是唯一的区别,因为将在运行时调用完全相同的函数,因此消费者指定它调用 C.Foo 是没有意义的。

表达式树记录的方法信息与常规函数调用记录的方法信息相同。 C# 规范对此几乎无话可说,它由实现定义(但 Microsoft 的实现似乎并未定义(记录)它):

Conversion of an anonymous function to an expression tree type produces an expression tree (§4.6). More precisely, evaluation of the anonymous function conversion leads to the construction of an object structure that represents the structure of the anonymous function itself. The precise structure of the expression tree, as well as the exact process for creating it, are implementation defined.

关于c# - MethodCallExpression.Method 总是返回根基类的 MethodInfo,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16419168/

相关文章:

c# - 带有子查询的表达式树

c# - 当我只有泛型类型和实例值时,如何手动构建表达式树?

c# - 如何创建 LINQ 表达式树以选择匿名类型

c# - 为组件和页面创建一个新的 Blazor 项目

c# - WPF 中的 MenuItem 仅注册右键单击,但不注册左键单击

c# - 有没有更简单的方法来解析和解决 C# 中的表达式?

c# - .NET Core 中的 *deps.json 文件

空条件运算符和等待的 C# 错误

c# - 注册来自不同类的回调

c# - 创建用于验证的属性并更改参数值