Почему компилятор С# не является кодом ошибки, когда статический метод вызывает метод экземпляра?

В следующем коде есть статический метод 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(...). Даже закрытая версия компилируется, когда невозможно, чтобы во время выполнения можно было вызвать любой метод, отличный от метода экземпляра.

Ответ 1

По какой-то причине разрешение перегрузки всегда находит наилучшее соответствие перед для статического и нестатического. Попробуйте этот код со всеми статическими типами:

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

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

Это не будет компилироваться, потому что лучшая перегрузка - та, которая принимает string. Но эй, это метод экземпляра, поэтому компилятор жалуется (вместо того, чтобы перехватить вторую лучшую перегрузку).

Дополнение: Поэтому я думаю, что объяснение примера dynamic исходного вопроса состоит в том, что для того, чтобы быть последовательным, когда типы являются динамическими, мы также first находим лучшую перегрузку (только для проверки номера параметров и типы параметров и т.д., а не статические или нестатические), и только , а затем проверяют статичность. Но это означает, что статическая проверка должна ждать времени выполнения. Следовательно, наблюдаемое поведение.

Позднее добавление: некоторые сведения о том, почему они решили делать то, что этот смешной порядок можно сделать из этого сообщения в блоге Эрика Липперта.

Ответ 2

Foo имеет параметр "x", который является динамическим, что означает, что Bar (x) является динамическим выражением.

Было бы вполне возможно, что для примера были бы такие методы, как:

static Bar(SomeType obj)

В этом случае правильный метод будет разрешен, поэтому оператор Bar (x) отлично действует. Тот факт, что существует метод экземпляра Bar (x), не имеет значения и даже не рассматривается: по определению, поскольку Bar (x) является динамическим выражением, мы отложили разрешение на время выполнения.

Ответ 3

"Динамическое" выражение будет привязано во время выполнения, поэтому, если вы определяете статический метод с правильной сигнатурой или методом экземпляра, компилятор не проверяет его.

"Правильный" метод будет определен во время выполнения. Компилятор не может знать, существует ли там допустимый метод во время выполнения.

"Динамическое" ключевое слово определено для динамических и script языков, где Метод может быть определен в любое время, даже во время выполнения. Сумасшедший материал

Здесь образец, который обрабатывает ints, но не содержит строки, из-за метода находится в экземпляре.

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