为什么 C # 编译器在静态方法调用实例方法时不出现错误代码?

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

public sealed class Example
{
int count;


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


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

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

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

在 VisualStudio2008中编辑1: *

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

3056 次浏览

The "dynamic" expression will be bound during runtime, so if you define a static method with the correct signature or a instance method the compiler will not check it.

The "right" method will be determined during runtime. The compiler can not know if there is a valid method there during runtime.

The "dynamic" keyword is defined for dynamic and script languages, where the Method can be defined at any time, even during runtime. Crazy stuff

Here a sample which handles ints but no strings, because of the method is on the instance.

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

You can add a method to handle all "wrong" calls, which could not be handled

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

Foo has a parameter "x" that is dynamic, which means Bar(x) is a dynamic expression.

It would be perfectly possible for Example to have methods like:

static Bar(SomeType obj)

In which case the correct method would be resolved, so the statement Bar(x) is perfectly valid. The fact that there is an instance method Bar(x) is irrelevent and not even considered: by definition, since Bar(x) is a dynamic expression, we have deferred resolution to runtime.

UPDATE: Below answer was written in 2012, before the introduction of C# 7.3 (May 2018). In What's new in C# 7.3, the section Improved overload candidates, item 1, it is explained how the overload resolution rules have changed so that non-static overloads are discarded early. So the below answer (and this entire question) has mostly only historical interest by now!


(Pre C# 7.3:)

For some reason, overload resolution always finds the best match before checking for static versus non-static. Please try this code with all static types:

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


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

This will not compile because the best overload is the one taking a string. But hey, that's an instance method, so compiler complains (instead of taking the second-best overload).

Addition: So I think the explanation of the dynamic example of the Original Question is that, in order to be consistent, when types are dynamic we also first find the best overload (checking only parameter number and parameter types etc., not static vs. non-static), and only then check for static. But that means that the static check has to wait until runtime. Hence the observed behavior.

Late addition: Some background on why they chose to do things this funny order can be inferred from this blog post by Eric Lippert.