为什么C#编译器不会在静态方法调用实例方法的地方提供故障代码?

问题描述 投票:108回答:3

下面的代码有一个静态方法Foo(),它调用一个实例方法Bar()

public sealed class Example
{
    int count;

    public static void Foo( dynamic x )
    {
        Bar(x);
    }

    void Bar( dynamic x )
    {
        count++;
    }
}

它编译时没有错误*,但在运行时生成运行时绑定程序异常。如预期的那样,将动态参数删除到这些方法会导致编译器错误。

那么为什么使用动态参数可以编译代码? ReSharper也不会将其显示为错误。

Edit 1: *在Visual Studio 2008中

Edit 2:添加了sealed,因为子类可能包含静态Bar(...)方法。当不可能在运行时调用实例方法以外的其他任何方法时,即使是密封版本也可以编译。

c# visual-studio-2008 dynamic compiler-errors
3个回答
70
投票

更新:以下答案写于2012年,在C#7.3引入之前(2018年5月)。在What's new in C# 7.3中的第[[改善的过载候选者]]部分的项目1中,说明了如何更改过载解决规则,以便尽早丢弃非静态过载。因此,以下答案(以及整个问题)目前基本上只具有历史意义!


((Pre C#7.3:)

出于某种原因,过载解析总是找到最佳匹配

之前

,检查静态与非静态。请尝试使用所有静态类型的代码:class SillyStuff { static void SameName(object o) { } void SameName(string s) { } public static void Test() { SameName("Hi mom"); } }
这不会编译,因为最好的重载是采用string的重载。但是,嘿,那是一个实例方法,因此编译器会抱怨(而不是承受次优的重载)。

添加:因此,我认为原始问题的dynamic示例的解释是,为了保持一致,当类型为动态时,我们还

first

找到最佳的重载(仅检查参数编号和参数类型等,而不是静态与非静态),并且仅then检查静态。但这意味着静态检查必须等到运行时才能执行。因此观察到的行为。
最新补充:从this blog post by Eric Lippert中可以推断出他们为什么选择做这种滑稽的事情的背景。

30
投票
Foo具有动态的参数“ x”,这意味着Bar(x)是动态表达式。

9
投票
“动态”表达式将在运行时绑定,因此,如果使用正确的签名或实例方法定义静态方法,编译器将不会对其进行检查。
© www.soinside.com 2019 - 2024. All rights reserved.