Для интерфейса добавление ключевых слов abstract
или даже public
ключевых слов было бы избыточным, поэтому вы их опускаете:
interface MyInterface {
void Method();
}
В CIL метод отмечен значком virtual
и abstract
.
(Обратите внимание, что Java позволяет объявлять элементы интерфейса public abstract
).
Для класса реализации есть несколько вариантов:
Не допускает переопределения : в C # класс не объявляет метод как virtual
. Это означает, что его нельзя переопределить в производном классе (только скрыть). В CIL метод по-прежнему виртуальный (но запечатанный), потому что он должен поддерживать полиморфизм относительно типа интерфейса.
class MyClass : MyInterface {
public void Method() {}
}
Переопределяемый : как в C #, так и в CIL используется метод virtual
. Он участвует в полиморфной отправке и может быть отменен.
class MyClass : MyInterface {
public virtual void Method() {}
}
Явный : это способ для класса реализовать интерфейс, но не предоставлять методы интерфейса в общедоступном интерфейсе самого класса. В CIL метод будет private
(!), Но он по-прежнему может быть вызван извне класса из ссылки на соответствующий тип интерфейса. Явные реализации также нельзя переопределить. Это возможно, потому что существует директива CIL ( .override
), которая свяжет частный метод с соответствующим методом интерфейса, который он реализует.
[C #]
class MyClass : MyInterface {
void MyInterface.Method() {}
}
[CIL]
.method private hidebysig newslot virtual final instance void MyInterface.Method() cil managed
{
.override MyInterface::Method
}
В VB.NET вы даже можете указать псевдоним имени метода интерфейса в классе реализации.
[VB.NET]
Public Class MyClass
Implements MyInterface
Public Sub AliasedMethod() Implements MyInterface.Method
End Sub
End Class
[CIL]
.method public newslot virtual final instance void AliasedMethod() cil managed
{
.override MyInterface::Method
}
Теперь рассмотрим этот странный случай:
interface MyInterface {
void Method();
}
class Base {
public void Method();
}
class Derived : Base, MyInterface { }
Если Base
и Derived
объявлены в одной сборке, компилятор сделает Base::Method
виртуальный и запечатанный (в CIL), даже еслиBase
не реализует интерфейс.
Если Base
и Derived
находятся в разных сборках, при компиляции Derived
сборки компилятор не изменит другую сборку, поэтому он представит член, в Derived
котором будет явная реализация, поскольку MyInterface::Method
он просто делегирует вызов Base::Method
.
Итак, вы видите, каждая реализация метода интерфейса должна поддерживать полиморфное поведение и, следовательно, должна быть помечена как виртуальная в CIL, даже если компилятор должен пройти через обручи, чтобы сделать это.
Да, методы реализации интерфейса являются виртуальными с точки зрения времени выполнения. Это деталь реализации, она заставляет интерфейсы работать. Виртуальные методы получают слоты в v-таблице класса, каждый слот имеет указатель на один из виртуальных методов. Приведение объекта к типу интерфейса генерирует указатель на раздел таблицы, реализующий методы интерфейса. Клиентский код, который использует ссылку на интерфейс, теперь видит указатель первого метода интерфейса со смещением 0 от указателя интерфейса и т. Д.
В своем первоначальном ответе я недооценил значение последнего атрибута. Это предотвращает переопределение виртуального метода производным классом. Производный класс должен повторно реализовать интерфейс, методы реализации затеняют методы базового класса. Этого достаточно для реализации языкового контракта C #, в котором говорится, что метод реализации не является виртуальным.
Если вы объявите метод Dispose () в классе Example как виртуальный, вы увидите, что последний атрибут удаляется. Теперь разрешаем производному классу переопределять его.
источник
В большинстве других сред скомпилированного кода интерфейсы реализованы как vtables - список указателей на тела методов. Обычно класс, реализующий несколько интерфейсов, будет иметь где-то в своем внутреннем компиляторе сгенерированные метаданные список интерфейсных vtables, по одной vtable на интерфейс (так что порядок методов сохраняется). Так обычно реализуются и COM-интерфейсы.
Однако в .NET интерфейсы не реализованы как отдельные таблицы vtables для каждого класса. Методы интерфейса индексируются через глобальную таблицу методов интерфейса, частью которой являются все интерфейсы. Следовательно, нет необходимости объявлять метод виртуальным, чтобы этот метод реализовал метод интерфейса - глобальная таблица методов интерфейса может просто напрямую указывать на кодовый адрес метода класса.
Объявление метода виртуальным для реализации интерфейса не требуется и на других языках, даже на платформах, отличных от CLR. Одним из примеров является язык Delphi в Win32.
источник
Они не виртуальные (с точки зрения того, как мы о них думаем, если не с точки зрения базовой реализации как (запечатанные виртуальные) - хорошо прочитать другие ответы здесь и самому узнать что-то :-)
Они ничего не отменяют - в интерфейсе нет реализации.
Все, что делает интерфейс, - это предоставляет «контракт», которого должен придерживаться класс - шаблон, если хотите, чтобы вызывающие абоненты знали, как вызвать объект, даже если они никогда раньше не видели этот конкретный класс.
Затем класс должен реализовать метод интерфейса, как он будет, в рамках контракта - виртуальный или «не виртуальный» (как оказалось, запечатанный виртуальный).
источник
Интерфейсы являются более абстрактным понятием, чем классы, когда вы объявляете класс, реализующий интерфейс, вы просто говорите, что «класс должен иметь эти конкретные методы из интерфейса, и не имеет значения, статические , виртуальные , не виртуальные , переопределяемые , поскольку если у него одинаковый идентификатор и параметры типа ".
Другие языки, поддерживающие интерфейсы, такие как Object Pascal («Delphi») и Objective-C (Mac), также не требуют, чтобы методы интерфейса были помечены как виртуальные, а не виртуальные.
Но, возможно, вы правы, я думаю, может быть хорошей идеей иметь в интерфейсах специальный атрибут «виртуальный» / «переопределить» на случай, если вы хотите ограничить методы классов, реализующие конкретный интерфейс. Но это также означает наличие ключевых слов «невиртуальный», «не опасно, не виртуально» для обоих интерфейсов.
Я понимаю ваш вопрос, потому что я вижу нечто подобное в Java, когда метод класса должен использовать «@virtual» или «@override», чтобы быть уверенным, что метод предназначен для виртуального использования.
источник
override
является ключевым словом первого класса в самом языке.