Васильев А.Н. Основы программирования на C#. Васильев А. Н. Программирование
Скачать 5.54 Mb.
|
Использование абстрактных классов Дядя Вова. Цаппу надо крутить, цаппу. — На Сам делай Мне нельзя, я чатланин. из к/ф «Кин-дза-дза» Для начала мы рассмотрим очень простой пример, в котором описывается абстрактный класса затем на основе этого класса создаются производные классы. Рассмотрим программу, представленную в листинге 1.1. Листинг 1.1. Знакомство с абстрактными классами System; // Ⱥɛɫɬɪɚɤɬɧɵɣ ɤɥɚɫɫ: abstract class Base{ // Ɂɚɳɢɳɟɧɧɨɟ ɰɟɥɨɱɢɫɥɟɧɧɨɟ ɩɨɥɟ: protected int num; // Ʉɨɧɫɬɪɭɤɬɨɪ: public Base(int n){ // ȼɵɡɨɜ ɦɟɬɨɞɚ: set(n); } // Ⱥɛɫɬɪɚɤɬɧɵɟ ɦɟɬɨɞɵ: public abstract void show(); public abstract void set(int n); public abstract int get(); Абстрактные классы и интерфейсы ɉɪɨɢɡɜɨɞɧɵɣ ɤɥɚɫɫ ɧɚ ɨɫɧɨɜɟ ɚɛɫɬɪɚɤɬɧɨɝɨ ɤɥɚɫɫɚ: class Alpha:Base{ // Ɂɚɳɢɳɟɧɧɨɟ ɰɟɥɨɱɢɫɥɟɧɧɨɟ ɩɨɥɟ: protected int val; // Ʉɨɧɫɬɪɭɤɬɨɪ: public Alpha(int n):base(n){ // ȼɵɡɨɜ ɦɟɬɨɞɚ: show(); } // ɉɟɪɟɨɩɪɟɞɟɥɟɧɢɟ ɚɛɫɬɪɚɤɬɧɨɝɨ ɦɟɬɨɞɚ: public override void show(){ // Ɉɬɨɛɪɚɠɟɧɢɟ ɫɨɨɛɳɟɧɢɹ: Console.WriteLine( ƎAlpha: {0}, {1} ɢ {2}Ǝ,num,val,get()); } // ɉɟɪɟɨɩɪɟɞɟɥɟɧɢɟ ɚɛɫɬɪɚɤɬɧɨɝɨ ɦɟɬɨɞɚ: public override void set(int n){ // ɉɪɢɫɜɚɢɜɚɧɢɟ ɡɧɚɱɟɧɢɣ ɩɨɥɹɦ: num=n; val=n%10; } // ɉɟɪɟɨɩɪɟɞɟɥɟɧɢɟ ɚɛɫɬɪɚɤɬɧɨɝɨ ɦɟɬɨɞɚ: public override int get(){ return num/10; } } // ɉɪɨɢɡɜɨɞɧɵɣ ɤɥɚɫɫ ɧɚ ɨɫɧɨɜɟ ɚɛɫɬɪɚɤɬɧɨɝɨ ɤɥɚɫɫɚ: class Bravo:Base{ // Ɂɚɳɢɳɟɧɧɨɟ ɰɟɥɨɱɢɫɥɟɧɧɨɟ ɩɨɥɟ: protected int val; // Ʉɨɧɫɬɪɭɤɬɨɪ: public Bravo(int n):base(n){ Глава 1 14 // ȼɵɡɨɜ ɦɟɬɨɞɚ: show(); } // ɉɟɪɟɨɩɪɟɞɟɥɟɧɢɟ ɚɛɫɬɪɚɤɬɧɨɝɨ ɦɟɬɨɞɚ: public override void show(){ // Ɉɬɨɛɪɚɠɟɧɢɟ ɫɨɨɛɳɟɧɢɹ: Console.WriteLine( ƎBravo: {0}, {1} ɢ {2}Ǝ,num,val,get()); } // ɉɟɪɟɨɩɪɟɞɟɥɟɧɢɟ ɚɛɫɬɪɚɤɬɧɨɝɨ ɦɟɬɨɞɚ: public override void set(int n){ // ɉɪɢɫɜɚɢɜɚɧɢɟ ɡɧɚɱɟɧɢɣ ɩɨɥɹɦ: num=n; val=n%100; } // ɉɟɪɟɨɩɪɟɞɟɥɟɧɢɟ ɚɛɫɬɪɚɤɬɧɨɝɨ ɦɟɬɨɞɚ: public override int get(){ return num/100; } } // Ʉɥɚɫɫ ɫ ɝɥɚɜɧɵɦ ɦɟɬɨɞɨɦ: class AbstractDemo{ // Ƚɥɚɜɧɵɣ ɦɟɬɨɞ: static void Main(){ // Ɉɛɴɟɤɬɧɚɹ ɩɟɪɟɦɟɧɧɚɹ ɚɛɫɬɪɚɤɬɧɨɝɨ ɤɥɚɫɫɚ: Base obj; // ɋɨɡɞɚɧɢɟ ɨɛɴɟɤɬɨɜ ɩɪɨɢɡɜɨɞɧɵɯ ɤɥɚɫɫɨɜ: Alpha A=new Alpha(123); Bravo B=new Bravo(321); // Ɉɛɴɟɤɬɧɨɣ ɩɟɪɟɦɟɧɧɨɣ ɛɚɡɨɜɨɝɨ ɤɥɚɫɫɚ ɩɪɢɫɜɚɢɜɚɟɬɫɹ // ɫɫɵɥɤɚ ɧɚ ɨɛɴɟɤɬ ɩɪɨɢɡɜɨɞɧɨɝɨ ɤɥɚɫɫɚ: obj=A; Console.WriteLine( Ǝɉɨɫɥɟ ɜɵɩɨɥɧɟɧɢɹ ɤɨɦɚɧɞɵ obj=AƎ); Абстрактные классы и интерфейсы // ȼɵɡɨɜ ɦɟɬɨɞɨɜ ɱɟɪɟɡ ɨɛɴɟɤɬɧɭɸ ɩɟɪɟɦɟɧɧɭɸ // ɛɚɡɨɜɨɝɨ ɤɥɚɫɫɚ: obj.set(456); obj.show(); // Ɉɛɴɟɤɬɧɨɣ ɩɟɪɟɦɟɧɧɨɣ ɛɚɡɨɜɨɝɨ ɤɥɚɫɫɚ ɩɪɢɫɜɚɢɜɚɟɬɫɹ // ɫɫɵɥɤɚ ɧɚ ɨɛɴɟɤɬ ɩɪɨɢɡɜɨɞɧɨɝɨ ɤɥɚɫɫɚ: obj=B; Console.WriteLine( Ǝɉɨɫɥɟ ɜɵɩɨɥɧɟɧɢɹ ɤɨɦɚɧɞɵ obj=BƎ); // ȼɵɡɨɜ ɦɟɬɨɞɨɜ ɱɟɪɟɡ ɨɛɴɟɤɬɧɭɸ ɩɟɪɟɦɟɧɧɭɸ // ɛɚɡɨɜɨɝɨ ɤɥɚɫɫɚ: obj.set(654); obj.show(); Результат выполнения программы следующий Результат выполнения программы (из листинга 1.1) Alpha: 123, 3 ɢ 12 Bravo: 321, 21 ɢ 3 ɉɨɫɥɟ ɜɵɩɨɥɧɟɧɢɹ ɤɨɦɚɧɞɵ obj=A Alpha: 456, 6 ɢ 45 ɉɨɫɥɟ ɜɵɩɨɥɧɟɧɢɹ ɤɨɦɚɧɞɵ obj=B Bravo: 654, 54 ɢ В программе описан абстрактный класс Base. Он описан с ключевым словом abstract. В классе объявлены три абстрактных метода (все описаны с ключевым словом abstract): метод show() без аргументов и не возвращающий результат, метод set() с целочисленным аргументом и не возвращающий результат, метод get() без аргументов и возвращающий целочисленный результат. Все эти методы должны быть описаны в производном классе, создаваемом на основе данного абстрактного класса. Ноне все содержимое абстрактного класса является абстрактным. В нем описано закрытое целочисленное поле num , а также конструктор с одним целочисленным аргументом. В теле Глава конструктора содержится команда set(n), которой метод set() вызывается с аргументом n, переданным конструктору. Интересно здесь то, что метод set() абстрактный ив классе Base не описана только объявлен. q ПОДРОБНОСТИ bХотя на основе абстрактного класса объект создать нельзя, но можно описать конструктор для абстрактного класса. Этот конструктор будет вызываться при создании объекта производного класса, поскольку в этом случае сначала вызывается конструктор базового класса. В нашем примере в теле конструктора класса Base вызывается абстрактный метод set() , в классе неописанный. Но проблемы при этом не возникает, поскольку выполняться конструктор базового класса будет при создании объекта производного класса, в котором метод set() должен быть описан. Проще говоря, на момент, когда метод set() будет вызываться, он уже будет описан. На основе класса Base создается два класса Alpha и Bravo. Эти классы очень похожи, но описываются по-разному. Начнем с общих моментов для обоих классов. Ив классе Alpha, ив классе Bravo появляется дополнительное целочисленное поле val. В каждом из классов описан конструктор с целочисленным аргументом, который передается конструктору базового класса. В теле конструктора вызывается метод show(). Но описывается метод show() в каждом классе со своими особенностями. В классе Alpha при вызове метода show() отображаются название класса Alpha, значения полей num и val и результат вызова метода get(). Метод show() для класса Bravo описан также, но название класса отображается другое. Метод) в каждом классе также свой. В классе Alpha метод get() описан так, что результатом возвращается значение num/10. Это значение поля num , если в нем отбросить разряд единиц. В классе Bravo результатом метода) является значение num/100, которое получается отбрасыванием разрядов единиц и десятков в значении поля Метод set() в классе Alpha переопределен таким образом, что при целочисленном аргументе n выполняются команды num=n и val=n%10. То есть полю num присваивается значение аргумента, а полю val в качестве значения присваивается остаток отделения значения аргумента на 10 (это последняя цифра в десятичном представлении числового значения аргумента В классе Bravo метод set() описан похожим образом, но полю val значение присваивается командой val=n%100 (остаток отделения Абстрактные классы и интерфейсы 17 значения аргумента на 100, или число из двух последних цифр в десятичном представлении значения аргумента n). { i НАЗ А МЕТКУ Обращаем внимание, что все абстрактные методы из базового класса в производном классе описываются с ключевым словом В главном методе программы мы командой Base obj объявляем объектную переменную obj абстрактного класса Base. Командами Alpha A=new Alpha(123) и Bravo B=new Bravo(321) создаются объекты производных классов. При этом в консольном окне появляются сообщения, содержащие название класса для созданного объекта, значения полей и результат вызова метода get() из созданного объекта ПОДРОБНОСТИ В сообщении, кроме имени класса, отображается еще три числа. Первое — это значение поля num . Второе — это значение поля Для объекта класса Alpha это последняя цифра в значении поля num , а для объекта класса Bravo это две последние цифры в значении поля num . Третье число — значение, возвращаемое методом get() . Для объекта класса Alpha это значение поля num без последней цифры, а для объекта класса Bravo это значение поля num без двух последних цифр. После выполнения команды obj=A объектной переменной базового абстрактного класса присваивается ссылка на объект производного класса Alpha . Далее командой obj.set(456) меняются значения полей объекта, после чего командой obj.show() проверяются значения полей и результат вызова метода Затем выполняется команда obj=B, при помощи которой объектной переменной базового абстрактного класса присваивается ссылка на объект производного класса Bravo. Командой obj.set(654) вносятся изменения в значения полей объекта, а командой obj.show() проверяется результат НАЗ А МЕТКУ Стоит заметить, что если мы вызываем через объектную переменную абстрактного базового класса переопределенные методы из объекта производного класса, то версия метода определяется на основе класса объекта, из которого вызывается метод Глава Еще один пример, который мы рассмотрим далее, дает представление о том, как описывается и используется абстрактный класс, в котором есть абстрактные свойства и индексаторы. Листинг 1.2. Абстрактные свойства и индексаторы using System; // Ⱥɛɫɬɪɚɤɬɧɵɣ ɤɥɚɫɫ: abstract class Base{ // Ⱥɛɫɬɪɚɤɬɧɨɟ ɬɟɤɫɬɨɜɨɟ ɫɜɨɣɫɬɜɨ: public abstract string text{ get; set; } // Ⱥɛɫɬɪɚɤɬɧɵɣ ɢɧɞɟɤɫɚɬɨɪ ɫ ɰɟɥɨɱɢɫɥɟɧɧɵɦ ɢɧɞɟɤɫɨɦ: public abstract char this[int k]{ get; } // Ⱥɛɫɬɪɚɤɬɧɨɟ ɰɟɥɨɱɢɫɥɟɧɧɨɟ ɫɜɨɣɫɬɜɨ: public abstract int length{ get; } } // ɉɪɨɢɡɜɨɞɧɵɣ ɤɥɚɫɫ ɧɚ ɨɫɧɨɜɟ ɚɛɫɬɪɚɤɬɧɨɝɨ: class Alpha:Base{ // Ɂɚɤɪɵɬɨɟ ɩɨɥɟ, ɹɜɥɹɸɳɟɟɫɹ ɫɫɵɥɤɨɣ ɧɚ ɦɚɫɫɢɜ: private char[] symbs; // Ʉɨɧɫɬɪɭɤɬɨɪ: public Alpha(string t):base(){ // Ɍɟɤɫɬɨɜɨɦɭ ɫɜɨɣɫɬɜɭ ɩɪɢɫɜɚɢɜɚɟɬɫɹ ɡɧɚɱɟɧɢɟ: text=t; } // ɉɟɪɟɨɩɪɟɞɟɥɟɧɢɟ ɬɟɤɫɬɨɜɨɝɨ ɫɜɨɣɫɬɜɚ: public override string text{ Абстрактные классы и интерфейсы get{ // Ɋɟɡɭɥɶɬɚɬɨɦ ɹɜɥɹɟɬɫɹ ɬɟɤɫɬɨɜɚɹ ɫɬɪɨɤɚ: return new string(symbs); } set{ // ɋɨɡɞɚɧɢɟ ɫɢɦɜɨɥɶɧɨɝɨ ɦɚɫɫɢɜɚ ɢ ɩɪɢɫɜɚɢɜɚɧɢɟ // ɡɧɚɱɟɧɢɹ ɩɨɥɸ: symbs=value.ToCharArray(); } } // ɉɟɪɟɨɩɪɟɞɟɥɟɧɢɟ ɰɟɥɨɱɢɫɥɟɧɧɨɝɨ ɫɜɨɣɫɬɜɚ: public override int length{ get{ // Ɋɚɡɦɟɪ ɦɚɫɫɢɜɚ: return symbs.Length; } } // ɉɟɪɟɨɩɪɟɞɟɥɟɧɢɟ ɢɧɞɟɤɫɚɬɨɪɚ: public override char this[int k]{ get{ // Ɂɧɚɱɟɧɢɟ ɷɥɟɦɟɧɬɚ ɫɢɦɜɨɥɶɧɨɝɨ ɦɚɫɫɢɜɚ: return symbs[k]; } } } // ɉɪɨɢɡɜɨɞɧɵɣ ɤɥɚɫɫ ɧɚ ɨɫɧɨɜɟ ɚɛɫɬɪɚɤɬɧɨɝɨ: class Bravo:Base{ // Ɂɚɤɪɵɬɨɟ ɬɟɤɫɬɨɜɨɟ ɩɨɥɟ: private string txt; // Ʉɨɧɫɬɪɭɤɬɨɪ: public Bravo(string t):base(){ // Ɍɟɤɫɬɨɜɨɦɭ ɫɜɨɣɫɬɜɭ ɩɪɢɫɜɚɢɜɚɟɬɫɹ ɡɧɚɱɟɧɢɟ: Глава 1 20 text=t; } // ɉɟɪɟɨɩɪɟɞɟɥɟɧɢɟ ɬɟɤɫɬɨɜɨɝɨ ɫɜɨɣɫɬɜɚ: public override string text{ get{ // Ɂɧɚɱɟɧɢɟ ɩɨɥɹ: return txt; } set{ // ɉɪɢɫɜɚɢɜɚɧɢɟ ɡɧɚɱɟɧɢɹ ɩɨɥɸ: txt=value; } } // ɉɟɪɟɨɩɪɟɞɟɥɟɧɢɟ ɰɟɥɨɱɢɫɥɟɧɧɨɝɨ ɫɜɨɣɫɬɜɚ: public override int length{ get{ // Ʉɨɥɢɱɟɫɬɜɨ ɫɢɦɜɨɥɨɜ ɜ ɬɟɤɫɬɟ: return txt.Length; } } // ɉɟɪɟɨɩɪɟɞɟɥɟɧɢɟ ɢɧɞɟɤɫɚɬɨɪɚ: public override char this[int k]{ get{ // ɋɢɦɜɨɥ ɜ ɬɟɤɫɬɟ: return txt[k]; } } } // Ʉɥɚɫɫ ɫ ɝɥɚɜɧɵɦ ɦɟɬɨɞɨɦ: class AbstrPropAndIndexDemo{ // Ƚɥɚɜɧɵɣ ɦɟɬɨɞ: static void Main(){ Абстрактные классы и интерфейсы // ɋɫɵɥɤɚ ɧɚ ɨɛɴɟɤɬ ɩɪɨɢɡɜɨɞɧɨɝɨ ɤɥɚɫɫɚ ɡɚɩɢɫɵɜɚɟɬɫɹ // ɜ ɨɛɴɟɤɬɧɭɸ ɩɟɪɟɦɟɧɧɭɸ ɛɚɡɨɜɨɝɨ ɤɥɚɫɫɚ: Base obj=new Alpha( ƎAlphaƎ); // Ɉɬɨɛɪɚɠɟɧɢɟ ɡɧɚɱɟɧɢɹ ɬɟɤɫɬɨɜɨɝɨ ɫɜɨɣɫɬɜɚ: Console.WriteLine(obj.text); // ɇɨɜɨɟ ɡɧɚɱɟɧɢɟ ɬɟɤɫɬɨɜɨɝɨ ɫɜɨɣɫɬɜɚ: obj.text= ƎBaseƎ; // ɂɧɞɟɤɫɢɪɨɜɚɧɢɟ ɨɛɴɟɤɬɚ: for(int k=0;k Ǝ|Ǝ+obj[k]); } Console.WriteLine( Ǝ|Ǝ); // ɋɫɵɥɤɚ ɧɚ ɨɛɴɟɤɬ ɩɪɨɢɡɜɨɞɧɨɝɨ ɤɥɚɫɫɚ ɡɚɩɢɫɵɜɚɟɬɫɹ // ɜ ɨɛɴɟɤɬɧɭɸ ɩɟɪɟɦɟɧɧɭɸ ɛɚɡɨɜɨɝɨ ɤɥɚɫɫɚ: obj=new Bravo( ƎBravoƎ); // ɂɧɞɟɤɫɢɪɨɜɚɧɢɟ ɨɛɴɟɤɬɚ: for(int k=0;k Ǝ|Ǝ+obj[k]); } Console.WriteLine( Ǝ|Ǝ); // ɇɨɜɨɟ ɡɧɚɱɟɧɢɟ ɬɟɤɫɬɨɜɨɝɨ ɫɜɨɣɫɬɜɚ: obj.text= ƎBaseƎ; // Ɉɬɨɛɪɚɠɟɧɢɟ ɡɧɚɱɟɧɢɹ ɬɟɤɫɬɨɜɨɝɨ ɫɜɨɣɫɬɜɚ: Console.WriteLine(obj.text); Ниже показано, как выглядит результат выполнения программы Результат выполнения программы (из листинга 1.2) Alpha |B|a|s|e| Глава В программе описан абстрактный класс Base, в котором объявлены два абстрактных свойства (текстовое text и целочисленное length) и индексатор с целочисленным индексом. Все эти члены класса описаны с ключевым словом abstract. В теле абстрактного текстового свойства указаны ключевые слова get и set (после каждого ключевого слова ставится точка с запятой. Это означает, что при переопределении по факту при описании) свойства должен быть описан и get-аксессор, и set-аксессор. В теле свойства length ив теле индексатора указано только ключевое слово get. Поэтому при переопределении свойства и индексатора в производном классе описывается только get-аксессор. { i НАЗ А МЕТКУ В производных классах свойства и индексатор описываются с ключевым словом override , как при переопределении методов. На основе класса Base путем наследования создается класс Alpha и класс Bravo. Классы практически идентичны, но имеются отличия на техническом уровне. В классе Alpha используется закрытое поле symbs , являющееся ссылкой на символьный массив. В классе Bravo описано закрытое текстовое свойство txt. У каждого из классов есть конструктор с текстовым аргументом НАЗ А МЕТКУ В базовом классе Base конструктор не описывался. В производных классах Alpha и Bravo описываются конструкторы. В них вызывается конструктор базового класса без аргументов (инструкция base() ). Имеется ввиду конструктор по умолчанию класса В каждом из конструкторов переданное аргументом текстовое значение присваивается свойству text. Нов классе Alpha процедура присваивания значения свойству text описана так, что присваиваемое текстовое значение с помощью библиотечного метода ToCharArray() преобразуется в массив и ссылка на этот массив записывается в поле symbs. В классе Bravo текстовое значение при присваивании свойству text в действительности записывается в поле txt. Значение поля txt возвращается в виде значения свойства text для объекта класса Bravo. Абстрактные классы и интерфейсы 23 Для объекта класса Alpha в качестве значения свойства text возвращается текстовая строка, сформированная на основе символьного массива symbs . Чтобы сформировать текст на основе символьного массива, мы создаем анонимный объект класса String и передаем ему аргументом ссылку на символьный массив НАЗ А МЕТКУ В команде return new string(symbs) в get -аксессоре свойства text класса Alpha мы использовали синоним string для инструкции Свойство length описано таким образом, что для объекта класса Alpha оно возвращает длину символьного массива symbs, а для объекта класса Bravo значение свойства определяется количеством символов в текстовом поле txt. Наконец, индексатор описан так, что результатом возвращается символьное значение элемента массива (класс Alpha) или символ из текста (класс В главном методе программы сначала командой Base obj=new Alpha( ƎAlphaƎ) создается объект класса Alpha, а ссылка на объект записывается в объектную переменную obj абстрактного класса. Мы проверяем значение свойства text (команда Console. WriteLine(obj.text) ), присваиваем свойству новое значение команда) и с помощью оператора цикла, индексируя объектную переменную obj, посимвольно отображаем содержимое символьного массива из объекта, на который ссылается переменная obj. После этого командой obj=new Bravo( ƎBravoƎ) создаем объект класса Bravo и записываем ссылку на него в переменную obj. В телеоператора цикла выполняется индексирование объекта, благодаря чему мы посимвольно отображаем содержимое текстового поля объекта, на который теперь ссылается переменная obj. Командой obj.text= ƎBaseƎ текстовому свойству объекта присваивается новое значение, после чего мы проверяем значение этого свойства (команда Что мы получили в данном случае На основе абстрактного класса мы создали два класса с одинаковым набором характеристик (имеются ввиду свойства и индексатор), но при этом механизм реализации производных классов разный. Получается, что абстрактный класс задал некоторый шаблон, в соответствии с которым реализованы производные классы. Такой подход на практике нередко оказывается полезным Глава Знакомство с интерфейсами Статуя здесь ни причем. Она тоже женщина несчастная. Она графа любит. из к/ф Формула любви» Выше мы познакомились с базовыми принципами использования абстрактных классов. Как уже несколько раз отмечалось, использование абстрактного класса в качестве базового позволяет создавать производные классы по одному шаблону — то есть с одинаковым набором свойств и методов. Вместе стем здесь имеется один тонкий момент. Дело в том, что в языке C# запрещено множественное наследование мы не можем создать производный класс на основе сразу нескольких базовых НАЗ А МЕТКУ Множественное наследование есть в языке C++. В языке C++ у производного класса может быть несколько базовых классов. В языках Java и C# от множественного наследования отказались в целях без- опасности. Это не очень хорошо, поскольку часто возникает необходимость объединить водно целое сразу несколько классов. Например, в языке C++, ставшем прародителем для языка C#, такая возможность существует. Это полезная возможность, но одновременно это и небезопасная возможность. Ведь разные классы описывались независимо друг от друга. Их объединение в один класс может привести к конфликтным ситуациям. Поэтому в языке C# от технологии множественного наследования отказались. Вместо множественного наследования используется другая технология, связанная с реализацией интерфейсов. Главная опасность в попытке объединения классов связана стем, что в них есть методы и эти методы каким-то образом определены. Когда метод описывался в классе, перспектива совместного использования этого метода с методами из иных классов, скорее всего, не рассматривалась. Отсюда и неприятные сюрпризы. Но если объединять классы с абстрактными методами, то данная проблема снимается автоматически, поскольку абстрактные методы не имеют тела, они только объявлены (ноне описаны. Необходимо только обеспечить, чтобы все методы были абстрактными. В обычном абстрактном классе в общем случае это не так. Отсюда появляется потребность в интерфейсах Абстрактные классы и интерфейсы 25 Интерфейс представляет собой блок из абстрактных методов, свойств и индексаторов. Фактически это аналог абстрактного класса. Нов отличие от абстрактного класса, в интерфейсе абсолютно все абстрактное. Описывается интерфейс специальным образом, хотя описание интерфейса и напоминает описание класса. Общий шаблон описания интерфейса представлен ниже (жирным шрифтом выделены ключевые элементы шаблона ɢɦɹ |