Реализация интерфейса

В списке предков класса сначала указывается его базовый класс, если он есть, а затем через запятую — интерфейсы, которые реализует этот класс. Таким обра­зом, в С# поддерживается одиночное наследование для классов и множествен­ное — для интерфейсов. Это позволяет придать производному классу свойства нескольких базовых интерфейсов, реализуя их по своему усмотрению. Например, реализация интерфейса IAction в классе Monster может выглядеть сле­дующим образом:

using System;

namespace ConsoleApplication1

{

interface IAction

{

void Draw();

int Attack(int a);

void Die();

int Power { get; }

}

class Monster: IAction

{

public void Draw()

{

Console.WriteLine("Здесь был " + name);

}

public int Attack(int ammo_)

{

ammo -= ammo_;

if (ammo > 0)

Console.WriteLine("Ба-бах!");

else

ammo = 0;

return ammo;

}

public void Die()

{

Console.WriteLine("Monster " + name + " RIP");

health = 0;

}

public int Power

{

get

{

return ammo * health;

}

}

//...

}

}

Естественно, что сигнатуры методов в интерфейсе и реализации должны полно­стью совпадать. Для реализуемых элементов интерфейса в классе следует указы­вать спецификатор public. К этим элементам можно обращаться как через объект класса, так и через объект типа соответствующего интерфейса. Этот объект должен содержать ссылку на класс, поддерживающий интерфейс. Естест­венно, что объекты типа интерфейса, так же как и объекты абстрактных классов, созда­вать нельзя.

Monster Vasia = new Monster(50, 50, "Вася"); // объект класса Monster

Vasia.Draw(); // результат: Здесь был Вася

IAction Actor = new Monster(10, 10, "Маша"); // объект типа интерфейса

Actor.Draw(); // результат: Здесь был Маша

Удобство второго способа проявляется при присваивании объектам типа IAction ссылок на объекты различных классов, поддерживающих этот интерфейс. На­пример, легко себе представить метод с параметром типа интерфейса. На место этого параметра можно передавать любой объект, реализующий интерфейс:

static void Act(IAction A)

{

A.Draw();

}

static void Main()

{

Monster Vasia = new Monster(50, 50, "Вася");

Act(Vasia);

//...

}

Существует второй способ реализации интерфейса в классе: явное указание име­ни интерфейса перед реализуемым элементом. Спецификаторы доступа при этом не указываются. К таким элементам можно обращаться в программе только через объект типа интерфейса, например:

class Monster: IAction

{

int IAction.Power

{

get

{

return ammo * health;

}

void IAction.Draw()

{

Console.WriteLine("Здесь был " + name);

}

//...

}

//...

IAction Actor = new Monster(10, 10, "Маша");

Actor.Draw(); // обращение через объект типа интерфейса

// Monster Vasia = new Monster(50, 50, "Вася");

// Vasia.Draw(); // ошибка!Таким образом, при явном задании имени реализуемого интерфейса соответст­вующий метод не входит в интерфейс класса. Это позволяет упростить его в том случае, если какие-то элементы интерфейса не требуются конечному пользовате­лю класса.

Кроме того, явное задание имени реализуемого интерфейса перед именем метода позволяет избежать конфликтов при множественном наследовании, если эле­менты с одинаковыми именами или сигнатурой встречаются более чем в одном интерфейсе1. Пусть, например, класс Monster поддерживает два интерфейса: один для управления объектами, а другой для тестирования:

interface ITest

{

void Draw();

}

interface IAction

{

void Draw();

int Attack(int a);

void Die();

int Power {get; }

}

class Monster: IAction, ITest

{

void ITest.Draw()

{

Console.WriteLine("Testing " + name);

}

void IAction.Draw()

{

Console.WriteLine("Здесь был " + name);

}

//...

}

Оба интерфейса содержат метод Draw с одной и той же сигнатурой. Различать их помогает явное указание имени интерфейса. Обращаться к этим методам можно, используя операцию приведения типа, например:

Monster Vasia = new Monster(50, 50, "Вася");

((ITest)Vasia).Draw(); // результат: Здесь был Вася

((IAction)Vasia).Draw(); //результат: Testing Вася

Впрочем, если от таких методов не требуется разное поведение, можно реали­зовать метод первым способом (со спецификатором public), компилятор не воз­ражает:

class Monster: IAction, ITest

{

public void Draw()

{

Console.WriteLine("Здесь был " + name);

}

//...

}

К методу Draw, описанному таким образом, можно обращаться любым способом: через объект класса Monster, через интерфейс IAction или ITest. Конфликт возникает в том случае, если компилятор не может определить из контекста обращения к элементу, элемент какого именно из реализуемых ин­терфейсов требуется вызвать. При этом всегда помогает явное задание имени интерфейса.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: