为什么C#编译器不会在动态参数的调用点检查方法的“静态性”?

22

为什么C#编译器不会告诉我这段代码是无效的?

class Program
{
    static void Main(string[] args)
    {
        dynamic d = 1;
        MyMethod(d);
    }

    public void MyMethod(int i) 
    {
        Console.WriteLine("int");
    }
}

因为我试图从静态方法调用非静态方法,所以对 MyMethod 的调用在运行时失败了。这是非常合理的,但为什么编译器在编译时不将其视为错误呢?

以下内容将不会编译

class Program
{
    static void Main(string[] args)
    {
        dynamic d = 1;
        MyMethod(d);
    }
}

尽管存在动态分派,编译器确实会检查MyMethod是否存在。为什么它不验证“静态性”呢?


请注意,这在C# 7.3中已更改;请参阅我在链接线程中的(编辑后的)答案。 - Jeppe Stig Nielsen
2个回答

14

这里的重载决议是动态的。可以在此代码片段中看到:

class Program {
    public static void Main() {
        dynamic d = 1.0;
        MyMethod(d);
    }

    public void MyMethod(int i) {
        Console.WriteLine("int");
    }

    public static void MyMethod(double d) {
        Console.WriteLine("double");
    }
}

工作正常。现在将1分配给d并注意运行时故障。编译器不能在编译时合理地模拟动态重载分辨率,因此它不会尝试。


2
但是如果没有名为"MyMethod"的实例方法,它应该能够生成编译错误????显然,编译器足够聪明,可以看到根本不存在 MyMethod"...所以你的答案还没有说服我...也许你可以再详细解释一下? :-) - Dan Byström
1
我不明白为什么(运行时)重载解析在静态方法中调用时会考虑实例方法。 - CodesInChaos
1
因为它试图生成良好的诊断信息。程序员是否忘记添加重载?还是忘记了静态关键字? - Hans Passant
哦。在静态方法的编译时重载解析中,也会考虑实例方法。从未知道过。好的,这解释了运行时的重载行为。嗯,再思考一下,这使得重载解析在实例方法和静态方法中工作方式完全相同,这可能是件好事。 - CodesInChaos
1
@Hans:我总是忘记在新方法上使用static关键字,所以我认为这不是一个特殊情况 :-)。我确实理解编译器通常无法决定在运行时将调用哪个方法。我正在寻找编译器不防止运行时错误的原因,例如“因为那将需要X,进而意味着Y和Z,导致矛盾/歧义”。谢谢你的帮助。 - Rune
显示剩余2条评论

0

当编译器发现对/使用dynamic类型变量的操作时,它将使用CallSite对象发出该信息。(CallSite对象存储有关调用的信息。)

在您的第一个示例中,它可以编译,因为编译器可以发出信息(例如调用类型、要调用的方法等)。在第二个代码中,您尝试调用不存在的方法,因此编译器无法为您发出IL代码。


谢谢您的时间。我并不是在问第二个例子为什么不能编译,我完全同意编译器可以为第一个例子生成IL代码。我的问题实际上是为什么它允许第一个例子?为什么它不帮助我记得添加MyMethod方法的静态关键字呢? - Rune

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接