Я думаю, что это особенно вероятно, чтобы сбить с толку людей, которые (как и я) узнали о виртуальных и не виртуальных методах в C ++ и об интерфейсах в Java, а позже должны были выяснить, как эти два могут взаимодействовать в C #.
Диспетчеризация методов через ссылку на интерфейс в Java (сравнительно) проста, поскольку все методы в Java являются виртуальными. Поэтому, конечно, тип среды выполнения экземпляра решает, что вызывается, и это все ... в Java .
Это означает, что некоторые более сложные отношения между экземпляром, именем метода и реализацией недоступны в Java, что в зависимости от вашей точки зрения может рассматриваться как «хорошая вещь» или «плохая вещь».
Несмотря на это, это одно из больших различий между Java и C #, потому что методы не обязательно должны быть виртуальными в C #, они не являются по умолчанию. И не вдаваясь в детали реализации, это приводит к ключевому пониманию:
Если метод не помечен virtual
в родительском классе, вы не можете пометить соответствующий (тот же имя и подпись) метод в дочернем классе как override
. И отдельно, если метод в дочернем классе не помечен как override
, то он ведет себя как new
независимо от того, был ли он виртуальным в базовом классе .
Итак ... если класс реализует интерфейс с не virtual
методом, используется ли виртуальная диспетчеризация, когда вы хотите вызвать метод через ссылку на интерфейс? Казалось бы, разумный способ сделать это. (Подумайте о том, как ссылка на интерфейс может в конечном итоге указывать на экземпляр определенного класса, который может быть недоступен при компиляции кода, использующего ссылку на интерфейс. Виртуальная диспетчеризация здесь имеет смысл.) Тем не менее, это не единственный способ .
НО
Это не имеет значения. Даже если это так, в C # virtual
не означает, что реализация дочернего класса должна переопределить метод базового класса; это просто означает, что может, если объявлено об этом. А если явно не объявить метод базового класса virtual
, вы не сможете скомпилировать дочерний класс с методом, который объявляет себя переопределением этого метода.
Обновление : в ответе с самым высоким рейтингом утверждается, что ключевое решение принимается во время компиляции, и хотя реализация может сделать это в конкретном примере, я не я не верю - просто потому, что этот подход не обобщает.
public void myMethod(I i) {
i.f();
}
При компиляции вышеуказанного метода компилятор абсолютно не имеет ни малейшего представления, какую реальную реализацию он должен вызывать. А при компиляции какой-то другой единицы кода со строкой
myMethod(new A());
компилятор не знает, что f()
определенно нужно будет разрешить. Таким образом, компилятор, обрабатывающий этот второй блок, может настроить информацию, которая будет использоваться выходными данными компиляции первого блока, при принятии решения о том, как отправлять любой данный метод.
Но, в конечном счете, решение о том, чтобы фактически выполнить данную реализацию метода, не может быть принято до времени выполнения; принимает ли это форму виртуальной отправки, или какое-то чудовище, основанное на отражении, или что-то в этом роде.
На уровне языка это всего лишь детали реализации. Указанное поведение является тем, что важно, и именно здесь отношение между ключевыми словами virtual
и override
становится ключевым.