Высокоуровневые методы информатики и программирования Лекция 14

advertisement
Высокоуровневые методы
информатики и
программирования
Лекция 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( ) {...} // неявное определение
}
…
//другие элементы класса
// можно вызывать и через
// интерфейс
IMyInterface obj;
obj = new MyClass( );
obj.Method1( );
// а можно и как методы
// класса
MyClass 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, не задается
//другие элементы класса
}
// в этом случае метод можно вызвать
// только через интерфейс
IMyInterface obj;
obj = new MyClass( );
obj.Method1( );
// у экземпляра класс
// вызывать эти методы уже нельзя
MyClass 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 2008
1. Реализация интерфейса
2. Создание интерфейса для
описанного класса
• в контекстном меню Refactor
выбрать команду Extract Interface...
• диалоговом окне Extract Interface
выделить те методы, которые войдут в
интерфейс
Сортировка элементов массива
• Имеется метод сортировки элементов массива:
Array.Sort (<массив>);
• Для использования данного метода
необходимо, чтобы класс элементов массива
поддерживал интерфейс IComparable.
Интерфейс IComparable
Включает только один метод:
int CompareTo ( Object obj )
• Если
– <0, данный объект меньше, чем obj
– =0, объекты равны
– >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
– =0, obj1 равен obj2
– >0, obj1 больше, чем obj2
• Используется с такими методами, как Array.Sort() и
Array.BinarySearch().
Array.Sort (<массив>, [ <comparer>]);
• Для использования данного интерфейса нужно создать объект
класса, который реализует данный интерфейс и передать его в
качестве второго параметра.
Вспомогательные классы с интерфейсом
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()
Download