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


следующий код имеет статический метод, Foo(), вызов метода экземпляра, Bar():

public sealed class Example
{
    int count;

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

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

он компилируется без ошибок* но создает исключение связующего во время выполнения во время выполнения. Устранение динамического параметра для этих методов вызывает ошибку компилятора, как и ожидалось.

так почему же наличие динамического параметра позволяет компилировать код? ReSharper также не показывает его как ошибку.

Edit 1: *в Visual Studio 2008

Edit 2: добавил sealed поскольку возможно, что подкласс может содержать статический Bar(...) метод. Даже запечатанная версия компилируется, когда невозможно, чтобы любой метод, кроме метода экземпляра, мог быть вызван во время выполнения.

3 108

3 ответа:

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

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

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

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

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

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

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

было бы вполне возможно, например, есть такие методы, как:

static Bar(SomeType obj)

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

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

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

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

здесь образец, который обрабатывает 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++;
    }
}