110

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

public sealed class Example
{
    int count;

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

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

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

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

编辑 1: * 在 Visual Studio 2008 中

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

4

3 回答 3

71

更新:以下答案写于 2012 年,在引入 C# 7.3 (May 2018) 之前。在C# 7.3 的新增功能中,改进的重载候选部分,第1 项中,解释了重载解决规则如何更改,以便及早丢弃非静态重载。因此,下面的答案(以及整个问题)现在大多只具有历史意义!


(C# 7.3 之前:)

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

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

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

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

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

后期添加:从Eric Lippert 的这篇博客文章中可以推断出他们为什么选择做这个有趣的顺序的一些背景。

于 2012-10-11T16:00:08.200 回答
30

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

Example 完全有可能拥有如下方法:

static Bar(SomeType obj)

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

于 2012-10-11T15:17:23.147 回答
9

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

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

“动态”关键字是为动态和脚本语言定义的,其中方法可以随时定义,甚至在运行时。疯狂的事情

这是一个处理整数但不处理字符串的示例,因为该方法在实例上。

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++;
    }
}
于 2012-10-11T15:14:05.270 回答