为什么C#编译器不是静态方法调用实例方法的错误代码?

Mik*_*ott 109 c# compiler-errors dynamic visual-studio-2008

以下代码有一个静态方法Foo(),调用实例方法,Bar():

public sealed class Example
{
    int count;

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

    void Bar( dynamic x )
    {
        count++;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

那么为什么有一个动态参数允许编译代码?ReSharper也没有将其显示为错误.

在Visual Studio 2008中编辑1:*

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

Jep*_*sen 71

出于某种原因,检查静态与非静态之前,重载决策始终找到最佳匹配.请尝试使用所有静态类型的代码:

class SillyStuff
{
  static void SameName(object o) { }
  void SameName(string s) { }

  public static void Test()
  {
    SameName("Hi mom");
  }
}
Run Code Online (Sandbox Code Playgroud)

这将无法编译,因为最好的重载是采取a string.但是,嘿,这是一个实例方法,所以编译器抱怨(而不是采取第二好的重载).

另外:所以我认为对dynamic原始问题的例子的解释是,为了保持一致,当类型是动态的时,我们也首先找到最佳的过载(仅检查参数号和参数类型等,而不是静态与非静态) -static),只有然后检查是否有静电.但这意味着静态检查必须等到运行时.因此观察到的行为.

后期补充:有关他们选择做这个有趣订单的原因的一些背景可以从Eric Lippert的博客文章中推断出来.

  • 我写了这部分C#编译器,Jeppe是对的.请投票.在检查给定方法是静态方法还是实例方法之前发生重载解析,在这种情况下,我们将重载决策推迟到运行时,因此也将静态/实例检查推迟到运行时.此外,编译器通过"尽力而为"来静态地查找绝对不全面的动态错误. (45认同)
  • @MikeScott我只是试着说服你,C#中的重载决策总是如下:(1)找到最佳匹配,不管静态/非静态.(2)现在我们知道要使用什么重载,**然后**检查静态.因此,当在语言中引入`dynamic`时,我认为C#的设计者说:"当它是一个`动态`表达式时,我们不会考虑(2)编译时." 因此,我的目的是想出为什么他们选择在运行时之前不检查静态与实例.我会说,这个检查发生在**绑定时**. (5认同)

Mar*_*ell 30

Foo的参数"x"是动态的,这意味着Bar(x)是一个动态表达式.

Example完全可以使用以下方法:

static Bar(SomeType obj)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,将解析正确的方法,因此语句Bar(x)完全有效.存在实例方法Bar(x)的事实是无关紧要的,甚至没有考虑:根据定义,由于Bar(x)是动态表达式,我们将延迟解析为运行时.

  • 但是当你取出实例Bar方法时,它就不再编译了. (14认同)
  • @Mike不可能不是重点; 重要的是它是否*必需*.动态的全部意义在于它不是编译器的工作. (2认同)

obe*_*eak 9

"动态"表达式将在运行时绑定,因此如果使用正确的签名或实例方法定义静态方法,编译器将不会检查它.

"右"方法将在运行时确定.编译器无法知道运行时是否存在有效方法.

"dynamic"关键字是为动态和脚本语言定义的,其中可以随时定义Method,即使在运行时也是如此.疯狂的事情

这里有一个处理整数但没有字符串的示例,因为该方法在实例上.

class Program {
    static void Main(string[] args) {
        Example.Foo(1234);
        Example.Foo("1234");
    }
}
public class Example {
    int count;

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

    public static void Bar(int a) {
        Console.WriteLine(a);
    }

    void Bar(dynamic x) {
        count++;
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以添加一个方法来处理无法处理的所有"错误"调用

public class Example {
    int count;

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

    public static void Bar<T>(T a) {
        Console.WriteLine("Error handling:" + a);
    }

    public static void Bar(int a) {
        Console.WriteLine(a);
    }

    void Bar(dynamic x) {
        count++;
    }
}
Run Code Online (Sandbox Code Playgroud)