Слайд 1Проверка класса объекта
Иногда требуется определить, является ли объект представителем определенного
класса. Для решения этой задачи используется ключевое слово is.
Figure f
= new Circle();
if (f is Circle) Console.WriteLine("Это окружность.");
Несмотря на то, что переменная f была объявлена как Figure, при помощи is удалось определить, что в неё была записана ссылка на объект, представляющий класс Circle.
Слайд 2Приведение типов классов
Для того, чтобы среда .NET воспринимала созданный объект
как экземпляр определенного класса, используется приведение типов классов:
Figure f =
new Circle();
Circle c = (Circle) f;
Также для приведения типов можно воспользоваться ключевым словом as:
Figure f = new Circle();
Circle c = f as Circle;
В обоих примерах указывается, что с объектом, ссылка на который записана в переменную f, следует работать как с экземпляром класса Circle. Чтобы не пользоваться этой записью постоянно, создается отдельная переменная c.
Слайд 3Интерфейсы
Часто возникает необходимость создавать неродственные классы (без общего предка), имеющие
свойства и методы, одинаковые по типу и имени. Для решения
этой задачи в C# используются интерфейсы.
Интерфейс – описание в абстрактной форме (без указания кода реализации), какими свойствами и методами должен обладать класс.
В отличие от классов, интерфейсы не имеют предков (в том числе Object) и модификаторов доступа (всегда считаются открытыми).
Слайд 4Интерфейсы
Описание интерфейса начинается с ключевого слова interface:
interface IPurse // Интерфейс
кошелька
{
int Sum { get; set; } // Сумма в кошельке
void
AddMoney(int sum); // Добавление суммы
int DecMoney(int sum); // Извлечение суммы
}
На данном примере видно, что переменные и методы в интерфейсе не предполагают указание модификаторов доступа, поскольку интерфейс создается для общедоступного использования его компонентов другими классами. В то же время, модификатор доступа может быть указан для самого интерфейса.
В интерфейсе можно описывать свойства, но нельзя объявлять переменные, поскольку это нарушит принцип абстрактности.
Слайд 5Интерфейсы
В .NET принято начинать имя нового интерфейса с литеры «I».
Интерфейс
может быть описан в том же пространстве имён, что и
классы, которые его используют:
namespace Program // Пространство имён
{
interface IPurse
{
// Описание интерфейса
}
class NewClass
{
// Описание класса
}
}
Однако рекомендуется помещать описание интерфейса в отдельный файл (Проект -> Add -> NewItem -> Interface).
Слайд 6Интерфейсы
В C# класс может иметь только одного класса-предка. В то
же время, класс может наследовать методы и свойства от любого
количества интерфейсов. Наследование интерфейса описывается следующим образом:
class Person : IPurse
{
// Описание класса
}
Поскольку класс Person по умолчанию является наследником класса Object, справедлива также следующая запись:
class Person : Object, IPurse
{
// Описание класса
}
Примечание. Имя класса-предка следует указывать первым.
Слайд 7Интерфейсы
В классе должна быть прописана реализация всех без исключения методов
и свойств его интерфейсов. При этом все методы и свойства
объявляются с модификатором доступа public. В противном случае компилятор выдаст ошибку.
class Person : IPurse
{
// Описание свойств и методов класса
int sum = 0; // Описание реализации интерфейса
public int Sum
{ get { return sum; }
set { sum = value; } }
public void AddMoney(int sum)
{ Sum += sum; }
public int DecMoney(int sum)
{ Sum -= sum;
return sum; }
}
Слайд 8Интерфейсы
Чтобы использовать интерфейс, достаточно обратиться к его свойствам и методам
через объект класса, наследовавшего интерфейс.
Person p = new Person(«Имя», «Фамилия»);
// Новая персона
p.AddMoney(1000); // Пополняем кошелек персоны
Для демонстрации работы интерфейса создадим класс MyPurse, в котором объявим следующие переменные:
class MyPurse
{
Person p = new Person(«Имя», «Фамилия»);
Object o = p;
IPurse i = p;
}
Слайд 9Интерфейсы
Добавим в класс MyPurse методы вывода на консоль суммы в
кошельке после увеличения и уменьшения его содержимого:
void IncreaseSum(int sum) //
Увеличиваем сумму в кошельке
{
if (o is IPurse)
{
( (IPurse) o ).AddMoney(sum);
Console.WriteLine( ( (IPurse) o ).Sum );
}
}
void DecreaseSum(int sum) // Уменьшаем сумму в кошельке
{
i.DecMoney(sum);
Console.WriteLine( i.Sum );
}
Слайд 10Интерфейсы
Таким образом, мы объявили в классе MyPurse три переменные:
p –
переменная базового класса Person, которая содержит ссылку на объект-представитель данного
класса.
o – переменная базового класса Object, содержащая ссылку на тот же объект, на который ссылается переменная p.
i – переменная, имеющая тип интерфейса IPurse, проинициализированная ссылкой на объект из переменной p.
Следует отметить, что мы не можем создать объект-экземпляр интерфейса, чтобы записать ссылку на него в переменную интерфейса. Однако мы можем присвоить переменной интерфейса ссылку на объект класса, который реализует данный интерфейс. Например:
IPurse i = new Person(«Имя», «Фамилия»);
Примечание. Данная строка указывает, что в переменную i записывается ссылка на новый объект (использовано new). В нашем же примере переменные p, o и i ссылаются на один и тот же объект.
Слайд 11Интерфейсы
В методе IncreaseSum() мы приводим переменную o к типу интерфейса
IPurse:
( (IPurse) o ).AddMoney(sum);
Ранее, работая с классами, мы бы записали
эту команду как:
( (Person) o ).AddMoney(sum);
Но в этом случае мы оказываемся привязаны к определенному классу.
Интерфейсы же позволяют работать с переменной, в которую может быть записана ссылка на экземпляр любого класса, реализующего данный интерфейс.
Если класс не реализует интерфейс, то возникнет ошибка. Поэтому мы предварительно производим проверку:
if (o is IPurse) { /* Код */ }
Слайд 12Интерфейсы
Следует также напомнить, что следующие два способа приведения типов являются
равносильными:
( (IPurse) o ).AddMoney(sum);
( o as IPurse ).AddMoney(sum);
В методе DecreaseSum()
мы приводим переменную o к типу интерфейса IPurse:
i.DecMoney(sum);
В этой строке мы вообще не проверяем, на объект какого класса ссылается переменная i. Метод сработает без ошибки в том случае, если данный объект реализует интерфейс IPurse.
Слайд 13Интерфейсы как параметры
Переменные интерфейсов могут быть переданы в методы в
качестве параметров. Следует также напомнить, что следующие два способа приведения
типов являются равносильными:
void DecMoney(IPurse i)
{
i.DecMoney(sum);
Console.WriteLine( i.Sum );
}
В данном примере метод DecMoney() получает интерфейс в качестве параметра. Это означает, что в метод в качестве параметра может быть передан объект любого класса, реализующего данный интерфейс IPurse. Например, можно уменьшить баланс конкретной персоны:
DecMoney(p);
Также методы могут возвращать значения интерфейсного типа.
Слайд 14Перегрузка методов интерфейсов
Возможна ситуация, когда в классе уже есть метод,
который по имени, количеству параметров и их типу совпадает с
методом интерфейса, наследуемого этим же классом.
В случае наличия в одном классе двух методов с одинаковой структурой заголовков, возникла бы ошибка перегрузки. Однако интерфейсы позволяют избежать подобной ситуации.
Пусть класс Person имеет метод добавления удвоенной суммы:
public void AddMoney(int sum)
{
Sum += 1,18 * sum;
}
Требуется описать реализацию одноименного метода из интерфейса IPurse, который должен увеличивать сумму кошелька без использования множителя:
void IPurse.AddMoney(int sum)
{
Sum += sum;
}
Слайд 15Перегрузка методов интерфейсов
Как видно из примера, указание на описание реализации
метода интерфейса осуществляется путем указания имени интерфейса и названия метода:
IPurse.AddMoney
Следует отметить, что в этом случае запрещено указывать модификатор доступа (даже public), иначе возникнет ошибка. Данный метод по умолчанию и так будет открытым.
Теперь создадим переменную и вызовем метод класса:
Person p = new Person(«Имя», «Фамилия»);
p.AddMoney(1000); // Увеличение суммы на 1180
Чтобы вызвать метод интерфейса, следует привести переменную к типу интерфейса:
( (IPurse) p ).AddMoney(1000); // Увеличение суммы на 1000
Даже если класс наследует несколько интерфейсов с одноименными методами, имеющими одинаковую структуру заголовков, при помощи данного способа обращения мы сможем различить их. Наследование нескольких интерфейсов:
class Person: IPurse, IAccount // Интерфейсы кошелька и счета
Слайд 16Наследование интерфейсов
Интерфейсы могут наследоваться друг от друга. Описание наследования аналогично
описанию наследования у классов. Интерфейс-наследник перенимает все методы и свойства
интерфейса-предка.
interface ISafe : IPurse // Интерфейс сейфа
{
bool Locked() // Проверка, закрыт ли сейф
{
get;
}
void Lock(); // Метод закрытия сейфа
void Unlock(); // Метод открытия сейфа
}
Интерфейсы поддерживают множественное наследование, но только для интерфейсов. Наследовать методы и свойства классов интерфейсы не могут.