Высокоуровневые методы информатики и программирования Лекция 14 Интерфейсы
План работы Абстрактные классы Понятие интерфейса – interface Реализация интерфейсов Использование интерфейсов Стандартные интерфейсы. Поддержка работы с интерфейсами в Visual Studio.
Абстрактный класс Если мы ни хотим, чтобы можно было создавать экземпляры класса, то его нужно объявить абстрактным – abstract public abstract class MyClass {... } … MyClass a = new MyClass(); // ошибка! Для абстрактного класса не могут быть созданы экземпляры; он используется для создания производных классов; Единственная реальная польза от абстрактного класса состоит в том, что он описывает общие элементы (поля, методы) для всех производных от него классов. public class ClassA : MyClass {... } // нет абстрактных методов … ClassA a = new ClassA(); // правильно!
Абстрактные методы Абстрактный класс может иметь абстрактные методы – методы только с заголовком, без описания public abstract class MyClass {... public abstract void MethodA( );... } абстрактный метод не может иметь модификатор virtual (он всегда виртуальный) класс, наследующего абстрактный класс, может реализовать лишь некоторые его абстрактные методы, оставаясь абстрактным классом; класс, наследующего абстрактный класс, может наследоваться только от одного класса. public abstract class ClassA : MyClass {... public void MethodA( ) { Console.Write(ClassA); }... }
Интерфейс Интерфейсы это еще один пользовательский тип. Интерфейсы позволяют описывать некоторые желательные свойства и методы, которыми должны обладать разные классы. Особенности интерфейсов: – можно описывать переменные такого типа, ссылочный тип; – нельзя создавать объекты ( экземпляры ) такого типа; – включает набор абстрактных открытых (public) методов. То, что интерфейс не может иметь реализации, означает, что все его методы и свойства являются абстрактными. Интерфейс описывает поведение, которое конкретный класс или структура может выбрать для реализации. Класс (или структура) может при необходимости поддерживать много интерфейсов, тем самым поддерживая множество стилей поведения. Интерфейс (interface) во много аналогичен именованному набору абстрактных методов.
Интерфейс Интерфейс – это открытый контракт между поставщиком услуг (методов) и потребителем этих услуг. Интерфейс позволяет организовать свободную связь между клиентом и объектом (т.е. можно указать, что требуемый объект поддерживает некоторый интерфейс). Интерфейс представляет собой полностью абстрактный класс, который может включать только открытые абстрактные – методы, – свойства, – индексаторы – события.
Описание интерфейса interface { имя_метода1( ); // имя_методаN( ); }
Реализация интерфейса Интерфейс может быть реализован в описании класса. Для этого нужно указать, что класс является наследником интерфейса: public class MyClass : IMyInterface { public void Method1( ) {...} public void Method2( ) {...} public void Method3( ) {...} //другие элементы класса } Если класс наследует интерфейс или интерфейсы, то в нем должны быть реализованы (неявно или явно) все методы входящие в данный интерфейс. При описании методов реализующих методы интерфейса, не нужно использовать такие модификаторы, как new или override.
Для работы с объектами с помощью интерфейса нужно создать экземпляр класса, реализующий этот интерфейс, и присвоить его переменной, типом которой является название интерфейса. Например: IMyInterface obj; obj = new MyClass( ); obj.Method1( ); или можно вызывать методы напрямую MyClass obj; obj = new MyClass( ); obj.Method1( );
Работа с интерфейсом Описание интерфейса - Методы интерфейса объявляются без указания модификаторов доступа. interface IScalable { void ScaleX(float factor); void ScaleY(float factor); } Реализация интерфейса (класс Map реализует – поддерживает интерфейс IScalable) – override не указывается class Map : IScalable { public void ScaleX(float factor) {...}; public void ScaleY(float factor) {...}; } Использование интерфейса Map map1 = new Map( ); map1.ScaleX(100f); map1.ScaleY(200f); IScale intrf; intrf = (IScale) map1; // преобразование к интерфейсу intrf.ScaleX(200f);
Соответствие интерфейса и абстрактного класса интерфейс public interface IMyInterface { int Method1( ); bool Method2( ); void Method3( ); } почти эквивалентен абстрактному классу public abstract class MyInterface { int abstract void Method1( ); bool abstract void Method2( ); void abstract void Method3( ); }
Различие абстрактного класса и интерфейса 1.Абстрактный класс может иметь данные и не абстрактные методы. Интерфейс может иметь только описания свойств и методов, без их реализации. 2.Класс С# может наследоваться только от одного базового класса, даже если он абстрактный. Но класс C#, может реализовать (иметь наследование) много интерфейсов. 3.Абстрактный класс может быть производным от любого другого класса или от одного и более интерфейсов. Интерфейс может наследоваться только от других интерфейсов. 4.Абстрактный класс может иметь не открытые элементы (private или protected) методы и свойства. Интерфейс имеет только открытые методы. 5.Абстрактный класс может иметь статические методы и поля. Интерфейс может иметь только методы. 6.Абстрактный класс может иметь конструктор. Интерфейс не может его иметь.
Ошибки описания интерфейса! public interface IPointy { // Ошибка! Интерфейс не может иметь полей! public int numbOfPoints; // Ошибка! Интерфейс не может иметь конструкторы! public IPointy() { numbOfPoints = 0;}; // Ошибка! Интерфейс не содержит реализации методов! byte GetNumberOfPoints() { return numbOfPoints; } }
Работа с интерфейсом Класс, наследующий интерфейс, обязан полностью реализовать все методы интерфейса. Объекты данного класса могут использоваться везде, где требуется данный интерфейс. Может быть множественное наследование от интерфейсов. В классе производном от интерфейса можно выполнить неявную и явную реализацию интерфейса.
Неявная реализация интерфейса // определение интерфейса public interface IMyInterface { void Method1( ); void Method2( ); } // реализация интерфейса public class MyClass : IMyInterface { public void Method1( ) {...} // не указывается, что это методы интерфейса public void Method2( ) {...} // неявное определение //другие элементы класса } … // а можно и как методы // класса MyClass obj; obj = new MyClass( ); obj.Method1( ); // можно вызывать и через // интерфейс IMyInterface obj; obj = new MyClass( ); obj.Method1( );
Явная реализация интерфейса public interface IMyInterface { void Method1( ); void Method2( ); } public class MyClass : IMyInterface { void IMyInterface.Method1( ) {...} // указывается, что это методы интерфейса void IMyInterface.Method2( ) {...} // тип доступа public или private, не задается //другие элементы класса } // у экземпляра класс // вызывать эти методы уже нельзя MyClass obj; obj = new MyClass( ); obj.Method1( ); // в этом случае метод можно вызвать // только через интерфейс IMyInterface obj; obj = new MyClass( ); obj.Method1( );
Определение и использование нескольких интерфейсов public interface IMyInterface { void Method1( ); void Method2( ); } public interface IMyOtherInterface { void Method3( ); } public class MyClass : IMyInterface,IMyOtherInterface { public void Method1( ) {...} public void Method2( ) {...} public void Method3( ) {...} } //Client-side code: IMyInterface obj1; IMyOtherInterface obj2; obj1 = new MyClass( ); obj1.Method1( ); obj2 = (IMyOtherInterface)obj1; obj2.Method3( );
Приведение к типу интерфейса Для применения интерфейса нужно выполнить преобразование объекта, который его реализует, в ссылочную переменную интерфейсного типа. Имеются два типа преобразования типа: неявное и явное. При простом присвоении интерфейсной переменной экземпляра класса выполняется неявное преобразование типов: IMyInterface obj; obj = new MyClass( ); obj.Method1( ); Если класс MyClass не реализует интерфейс IMyInterface, то при компиляции будет выдаваться сообщение об ошибке, так как компилятор может читать метаданные класса и поэтому может заранее определить, реализует ли класс заданный интерфейс. Существуют ситуации, когда нельзя использовать неявное преобразование. В этих случаях нужно использовать явное преобразование (кастинг): IMyInterface obj; //... obj = (IMyInterface)new MyClass( ); obj.Method1( ); Если объект, к которому применяется явное преобразование, не поддерживает требуемый интерфейс, то во время выполнения будет формироваться исключение (exception) и если его не обработать, то работа программы завершаться аварийно.
Проверка на поддержку интерфейса Для того, чтобы избежать таких ситуаций нужно использовать операцию as. Например: IMyInterface obj; MyClass с = new MyClass( ); obj = с as IMyInterface; Операция as выполняет преобразование к требуемому интерфейсу, если объект поддерживает этот интерфейс, и присваивает полученное значение переменной. Если преобразование невозможно, то вместо генерации исключения (как это происходит при кастинге), данная операция присваивает интерфейсной переменной значение null. Например: SomeType obj1; IMyIntee obj2; // некоторый код для инициализации obj1 obj2 = obj1 as IMyInterface; if(obj2 != null) { obj.Method1( ); //переменная имеет верное значение } else { // объект obj1 не ддерживает интерфейс IMyIntee //обработка ошибки }
Операторы проверки интерфейсов оператор is (true, если в классе реализован интерфейс, иначе false) is – Например if (d is IScalable) {…} оператор as (получение ссылки на заданный интерфейс, если интерфейса нет, то null) as – Например IScalable s = d as IScalable; if (s != null) { … }
Пример использования SomeType obj1; IMyInterface obj2; /* Some code to initialize obj1 */ obj2 = obj1 as IMyInterface; if(obj2 != null) { obj.Method1( ); } else { // обработка ошибки – нет интерфейса }
Работа с интерфейсами в Visual Studio Реализация интерфейса 2. Создание интерфейса для описанного класса в контекстном меню Refactor выбрать команду Extract Interface... диалоговом окне Extract Interface выделить те методы, которые войдут в интерфейс
Сортировка элементов массива Имеется метод сортировки элементов массива: Array.Sort ( ); Для использования данного метода необходимо, чтобы класс элементов массива поддерживал интерфейс IComparable.
Интерфейс IComparable Включает только один метод: intint CompareTo ( Object obj )Object Если – 0, данный объект больше, чем obj
Упорядочение объектов класса Point с использованием интерфейса IComparable public class Point : IComparable { int IComparable.CompareTo(object obj) { Point p = (Point)obj; if(this.x == p.x) return 0; if(this.x > p.x) return 1; else return -1; }... }
Упорядочение объектов класса Person с использованием интерфейса IComparable public class Person : IComparable { string firstName, lastName; public int CompareTo(object obj) { Person otherPerson = (Person)obj; if (this.lastName != otherPerson.lastName) return this.lastName.CompareTo(otherPerson.lastName); else return this.firstName.CompareTo (otherPerson.firstName); } public Person(string _firstName, string _lastName){ firstName = _firstName; lastName = _lastName; } override public string ToString() { return firstName + " " + lastName; }
Упорядочение объектов класса Car с использованием интерфейса IComparable public class car : IComparable { // поля класса private int year; private string make; // конструктор public car(string Make,int Year) { make=Make; year=Year; } // свойства public int Year { get {return year;} set {year=value;} } public string Make { get {return make;} set {make=value;} } // Реализация интерфейса IComparable // метод CompareTo для сортировки по умолчанию. int IComparable.CompareTo(object obj) { car c=(car)obj; return String.Compare(this.make,c.make); }
Интерфейс IComparer Включает только один метод, который сравнивает два объекта: int Compare(object obj1, object obj2); Если – 0, obj1 больше, чем obj2 Используется с такими методами, как Array.Sort() и Array.BinarySearch(). Array.Sort (, [ ]); Для использования данного интерфейса нужно создать объект класса, который реализует данный интерфейс и передать его в качестве второго параметра.
Вспомогательные классы с интерфейсом IComparer // Вспомогательный класс для сортировки по увеличению свойства Year. class sortYearAscending : IComparer { int IComparer.Compare(object a, object b) { car c1 = (car)a; car c2 = (car)b; if (c1.Year > c2.Year) return 1; if (c1.Year < c2.Year) return -1; else return 0; } // Вспомогательный класс для сортировки по уменьшению свойства Make. class sortMakeDescending : IComparer { int IComparer.Compare(object a, object b) { car c1 = (car)a; car c2 = (car)b; return String.Compare(c2.Make, c1.Make); }
Пример использования интерфейса IComparer static void Main(string[] args) { // Создаем массив объектов car. car[] arrayOfCars = new car[6]{ new car("Ford",1992), new car("Fiat",1988), new car("Buick",1932), new car("Ford",1932), new car("Dodge",1999), new car("Honda",1977)}; // выводим не отсортированный массив. foreach (car c in arrayOfCars) Console.WriteLine(c.Make + "\t\t" + c.Year); // Сортируем с использованием IСomparable (по умолчанию). Array.Sort(arrayOfCars); Console.WriteLine("\nМассив отсортирован по возрастанию Make (IComparable)\n"); foreach (car c in arrayOfCars) Console.WriteLine(c.Make + "\t\t" + c.Year); // Сортировка по возрастанию Year с помощью IСomparer. Array.Sort (arrayOfCars, new sortYearAscending()); Console.WriteLine("\nМассив отсортирован по убыванию Year(IComparer)\n"); foreach (car c in arrayOfCars) Console.WriteLine(c.Make + "\t\t" + c.Year); // Сортировка по убыванию Make с помощью IComparer. Array.Sort(arrayOfCars, new sortMakeDescending()); Console.WriteLine("\nМассив отсортирован по убыванию Make(IComparer)\n"); foreach (car c in arrayOfCars) Console.WriteLine(c.Make + "\t\t" + c.Year); Console.ReadLine(); }
Интерфейс ICloneable Один метод, который возвращает копию текущего объекта Object Clone()