Содержание
Интерфейс во многом похож на класс, но он является скорее описанием чем реализацией методов. Особенности интерфейсы сводятся к двум пунктам:
- Все члены интерфейса являются абстрактными. Причем таковыми они являются по умолчанию, т.е. использовать ключевое слово
abstract
не нужно. - Класс (или структура) может реализовывать несколько интерфейсов (а вот наследовать класс только один)
Объявление интерфейса похоже на объявление класса, но интерфейс не содержит реализации своих членов (т.к. все они по умолчанию абстрактные). Члены интерфейса реализуются классом или структурой, которые реализую интерфейс. Интерфейс может содержать методы, свойства, события и индексаторы, т.е. те члены класса, которые могут быть абстрактными.
1 2 3 4 5 | public interface IEnumerator { bool MoveNext(); object Current { get; } } |
Члены интерфейса всегда по умолчанию публичные (public
) и не могут объявляться с модификатором доступа. Члены класса, реализующие члены интерфейса, также должны быть публичными (использование модификатора public
обязательно).
1 2 3 4 5 6 | internal class Countdown : IEnumerator { int count = 11; public bool MoveNext() { return count-- > 0 ; } public object Current { get { return count; } } } |
Объект, реализующий интерфейс, может быть приведен к типу интерфейса:
1 2 3 | IEnumerator e = new Countdown(); while (e.MoveNext()) Console.Write (e.Current); // 109876543210 |
Расширение интерфейса
Интерфейс может происходить (наследоваться) из другого интерфейса.
1 2 | public interface IUndoable { void Undo(); } public interface IRedoable : IUndoable { void Redo(); } |
Явная реализация интерфейса
Реализация нескольких интерфейсов может привести к конфликту между именами членов. Такой конфликт может быть разрешен с помощью явной реализации члена интерфейса:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | interface I1 { void Foo(); } interface I2 { int Foo(); } public class Widget : I1, I2 { public void Foo() // Неявная реализация { Console.Write ("Widget's implementation of I1.Foo"); } int I2.Foo() // Явная реализация { Console.Write ("Widget's implementation of I2.Foo"); return 42; } } |
Поскольку оба реализуемых интерфейса имеют одинаковые имена членов, класс Widget
явно реализует метод Foo
интерфейса I2
. Это позволяет двум методам сосуществовать в одном классе.
Явная реализация также позволяет скрывать члены класса от доступа из вне класса. При явной реализации нельзя использовать модификаторы доступа, а явно реализованные члены всегда частные (private
). Единственные способ вызвать явно реализованный интерфейс — привести экземпляр к типу интерфейса:
1 2 3 4 | Widget w = new Widget(); w.Foo(); ((I1)w).Foo(); ((I2)w).Foo(); |
Виртуальная реализация членов интерфейса
Неявно реализованный член интерфейса по умолчанию является запечатанным (sealed
), т.е. не может быть переопределен в производных классах. Однако, реализуя такой член в базовом классе, его можно пометить как virtual
или abstract
и тогда его можно будет переопределить в производном классе.
Явно реализованный член интерфейса не может быть помечен как virtual
и не может быть переопределен, но может быть перереализован (reimplemented).
Перереализация интерфейса в производном классе
Производный класс может перереализовать любой член интерфейса, уже реализованный в базовом классе. Перереализация переопределяет ранее произведенную реализацию, независимо от того, помечен ли член как виртуальный в базовом классе.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public interface IUndoable { void Undo(); } public class TextBox : IUndoable { void IUndoable.Undo() { Console.WriteLine ("TextBox.Undo"); } } public class RichTextBox : TextBox, IUndoable { public new void Undo() { Console.WriteLine ("RichTextBox.Undo"); } } |
В примере класс TextBox
явно реализует метод интерфейса IUndo.Undo
, в силу чего этот метод не может быть помечен как virtual
. Чтобы переопределить его класс RichTextBox
должен перереализовать метод интерфейса.