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

时间:2012-10-11 15:05:08

标签: c# visual-studio-2008 dynamic compiler-errors

以下代码使用静态方法Foo(),调用实例方法Bar()

public sealed class Example
{
    int count;

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

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

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

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

在Visual Studio 2008中编辑1: *

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

3 个答案:

答案 0 :(得分:70)

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

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

  public static void Test()
  {
    SameName("Hi mom");
  }
}

这不会编译,因为最好的重载是string。但是,嘿,这是一个实例方法,所以编译器抱怨(而不是采取次优的重载)。

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

延迟补充:可以从this blog post by Eric Lippert推断出他们选择做这个有趣订单的原因的一些背景知识。

答案 1 :(得分:30)

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

示例完全可以使用以下方法:

static Bar(SomeType obj)

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

答案 2 :(得分:9)

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

将在运行时确定“正确”方法。编译器无法知道运行时是否存在有效方法。

“dynamic”关键字是为动态和脚本语言定义的,其中可以随时定义Method,即使在运行时也是如此。 Crazy stuff

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

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++;
    }
}

您可以添加一种方法来处理无法处理的所有“错误”调用

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++;
    }
}