Явная реализация интерфейса
Явная реализация интерфейса
В определении IDraw3D мы были вынуждены назвать наш единственный метод Draw3D(), чтобы избежать конфликта с абстрактным методом Draw(), определенным в базовом классе Shape. Такое определение интерфейса вполне допустимо, но более естественным именем для метода было бы Draw().
// Изменение имени с "Draw3D" на "Draw".
public interface IDraw3D {
void Draw();
}
Если вносить такое изменение, то потребуется также обновить нашу реализацию DrawIn3D().
public static void DrawIn3D(IDraw3D itf3d) {
Console.WriteLine("-› Отображение IDraw3D-совместимоuо типа");
itf3d.Draw();
}
Теперь предположим, что мы определили новый класс Line (линия), который получается из абстрактного класса Shape и реализует iDraw3D (оба из них теперь определяют одинаково названные абстрактные методы Draw()).
// Проблемы? Это зависит.…
public class Line: Shape, IDraw3D {
public override void Draw() {
Console.WriteLine("Отображение линии…");
}
}
Класс Line компилируется беспрепятственно. Рассмотрим следующую логику Main().
static void Main(string[] args) {
…
// Вызов Draw().
Line myLine = new Line();
myLine.Draw();
// Вызов той же реализации Draw()!
IDraw3D itfDraw3d = (IDraw3D)myLine;
itfDraw3d.Draw();
}
С учетом того, что вы уже знаете о базовом классе Shape и интерфейсе IDraw3D, это выглядит так как будто вы вызываете два варианта метода Draw() (один с объектного уровня, а другой – с помощью интерфейсной ссылки). Однако компилятор способен вызывать одну и ту же реализацию и с помощью интерфейса, и с помощью объектной ссылки, поскольку абстрактный базовый класс Shape и интерфейс IDraw3D имеют одинаково названные члены. Это может оказаться проблемой, когда вы хотите, чтобы метод IDraw3D.Draw() представлял тип во всей трехмерной (3D) "красе", а не в неказистом двухмерном представлении переопределённого метода Shape.Draw().
Теперь рассмотрим родственную проблему. Что делать, если вам нужно гарантировать, что методы, определенные данным интерфейсом, будут доступны только с помощью интерфейсных ссылок, а не объектных? В настоящий момент члены, определенные интерфейсом IPointy, доступны как с помощью объектных ссылок, так и по ссылке на IPointy.
Ответ на оба вопроса дает явная реализация интерфейса. Используя этот подход, вы можете гарантировать, что пользователь объекта сможет получить доступ К методам, определенным данным интерфейсом, только с помощью правильной интерфейсной ссылки, не допуская при этом конфликта имен. Для примера рассмотрите следующий обновленный класс Line (предполагая, что вы соответствующим образом обновили Hexagon и Circle).
// Используя явную реализацию метода, можно указать
// другие реализации Draw() .
public class Line: Shape, IDraw3D {
// Этот метод можно вызвать только ссылкой на интерфейс IDraw3D.
void IDraw3D.Draw() { Console.WriteLine("Отображение ЗD-линии…"); }
// Это можно вызвать только на уровне объекта.
public override void Draw() { Console.WriteLine("Отображение линии…"); }
…
}
Как видите, при явной реализации члена интерфейса общий шаблон выглядит так: возвращаемоеЗначение ИмяИнтерфейса.ИмяМетода(аргументы). Здесь есть несколько "подводных камней", о которых следует знать. Прежде всего, не допускается определять явно реализуемые члены с модификаторами доступа. Например, следующий синтаксис недопустим.
// Нет! Это недопустимо.
public class Line: Shape, IDraw3D {
public void IDraw3D.Draw() { // ‹= Ошибка!
Console.WriteLine("Отображение 3D-линии…");
}
…
}
Главной причиной использования явной реализации метода интерфейса является необходимость "привязки" соответствующего метода интерфейса к уровню интерфейса. Если добавить ключевое слово public, то это будет означать, что данный метод является членом открытого сектора класса, и "привязка" будет отменена. Тогда вызывающая сторона сможет вызывать только метод Draw(), определенный базовым классом Shape на объектном уровне.
// Здесь вызывается переопределенный метод Shape.Draw().
Line myLine = new Line();
myLine.Draw();
Чтобы вызвать метод Draw(), определенный с помощью IDraw3D, мы должны явно получить интерфейсную ссылку, используя любой из ранее указанных подходов. Например:
// Это обеспечит вызов метода IDraw3D.Draw().
Line myLine = new Line();
IDraw3D i3d = (IDraw3D) myLine;
i3d.Draw();