Наследование интерфейсов и сокрытие членов

Как упоминалось ранее, интерфейсы поддерживают множественное наследование от других интерфейсов в синтаксическом смысле. Как и с множественным наследованием в С++, можно также строить ромбовидные решеточные иерархии, что показано в следующем коде:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface IUIControl
{
void Paint () ;
}
public interface IEditBox : IUIControl
{
}
public interface IDropList : IUIControl
{
}
public class ComboBox : IEditBox, IDropList {
public void Paint ()  {
// рисовать реализацию ComboBox
}

В этом примере оба интерфейса IEditBox и IDropList реализуют интерфейс IUIControl. И поскольку ComboBox реализует оба эти интерфейса, он должен реализовать объединение всех методов, объявленных в интерфейсах, которые он реализует непосредственно, плюс методы тех интерфейсов, от которых они наследуются, и т.д. В данном случае сюда относится только метод IUIControl. Paint.

Все довольно просто: методы из всех интерфейсов объединяются вместе в одно большое объединение, формируя набор методов, который должен реализовать конкретный класс или структура. Поэтому класс ComboBox получает только одну реализацию метода Paint. Если выполнить приведение экземпляра ComboBox к ссылке IEditBox и ссылке IdropList, то вызов Paint через любую из них приведет к вызову одной и той же реализации метода.

Если вы пришли из мира “родного” С++, то вам должна быть знакома вся сложность множественного наследования и диаграмм ромбовидных решеточных иерархий наследования, а также их связь с виртуальным наследованием в С++ и множеством генерируемых компилятором таблиц виртуальных методов. Чтобы понять, в чем состоит отличие С#, представьте, что С# сливает все эти виртуальные таблицы во время компиляции в одну большую таблицу.

Иногда — хотя и крайне редко — в интерфейсе требуется объявить метод, который скрывает метод унаследованного интерфейса. Чтобы предотвратить при этом выдачу предупреждения компилятором, можно воспользоваться ключевым словом new.

Традиционно объектно-ориентированный анализ и проектирование (OOA-D) считает примером плохого дизайна сокрытие невиртуальных унаследованных членов. Реализация, которая в действительности вызывается, зависит от типа ссылки, даже если две ссылки указывают на один и тот же экземпляр. Например, если A. DoWork — невиртуальный метод, и В наследуется от А и вводит новый метод В. DoWork, который скрывает базовый, то вызов DoWork на ссылке типа В вызовет в. DoWork, а вызов того же метода на ссылке типа А, полученной приведением ссылки на В к типу А, вызовет A. DoWork.

Такое поведение не интуитивно для объектно-ориентированных систем. Только потому, что язык позволяет вам делать что-то, это вовсе не означает, что делать это — правильно. Теперь вы видите, зачем прежде всего необходимы предупреждения компилятора.

В следующем примере IEditBox по той или иной причине должен объявлять метод Paint, чья сигнатура в точности совпадает с сигнатурой метода из IUIControl.

Поэтому здесь необходимо использовать ключевое слово new:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;
public interface IUIControl
{
void Paint ();
public interface IEditBox : IUIControl
{
new void Paint ();
}
public interface IDropList : IUIControl
{
}
public class ComboBox : IEditBox, IDropList
{
public void Paint ()  {
Console.WriteLine ( "ComboBox.IEditBox.Paint ()" );
}
}
public class EntryPoint
{
static void Main()  {
ComboBox cb = new ComboBox();
cb. Paint () ;
((IEditBox)cb).Paint ();
((IDropList)cb).Paint();
((IUIControl)cb).Paint ();
}

При всех вызовах Paint в методе Main они всегда превращаются в вызовы ComboBox. Paint. Это потому, что все обязательные методы, которые ComboBox должен реализовать, сливаются в один большой набор. Обе сигнатуры Paint — одна из IEditBox, и одна из IUIControl — сливаются в один элемент обязательного списка. В конце оба они отображаются на ComboBox.Paint. Вы можете изменить такое поведение, применив явную реализацию интерфейса, где ComboBox может выбирать между двумя разными версиями Paint — одной из IEditBox, и одной из IUIControl.

Когда интерфейс IEditBox объявляет метод Paint, используя ключевое слово new, тем самым он скрывает метод Paint, объявленный в IUIControl. Когда вы вызываете ComboBox. Paint, то вызывается метод IEditBox.Paint, как если выбран IEditBox-путь в иерархии наследования.

По сути, в любой момент, когда любой путь скрывает метод, то он скрывает метод для всех путей..

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100