2013-08-07 2 views
2

Как добавить защищенный виртуальный метод в класс «Компонент», чтобы его можно было вызвать из «Композитного»?Композитный шаблон в C++ и C# - защищенные виртуальные методы

В качестве конкретного примера рассмотрим приведенный ниже код и, пожалуйста, расскажите, как избежать ошибки компилятора в DxCompositeShape.ComputeSize.

abstract class DxShape // this is the Component 
{ 
    public abstract void Paint(); 
    protected abstract void ComputeSize(); 
} 

class DxCompositeShape : DxShape // this is the Composite 
{ 
    public readonly IList<DxShape> Shapes = new List<DxShape>(); 

    public override void Paint() 
    { 
     this.ComputeSize(); 
    } 

    protected override void ComputeSize() 
    { 
     foreach (DxShape sh in Shapes) 
     { 
      sh.ComputeSize(); // compiler error CS1540 
     } 
     // and some other logic here 
    } 
} 

EDIT: Я изменил мой образец, так что я ComputeSize вместо Init (люди полагают, что Init всегда можно назвать в конструкторе).

+2

Итак, вы даете нам код C#, но ваш вопрос касается как C++, так и C#? Зачем? –

+0

@SebastianRedl Если код переведен на C++, у нас та же проблема. –

+1

Если код C++, вы можете использовать 'friend'. – Xaqq

ответ

1

Создать невиртуальный функцию Initialise() в базовом классе, который вызывает Init

например:

abstract class DxShape 
{ 
    protected void Initialise() 
    { 
     Init(); 
    } 
    protected abstract void Init(); 
    //... 
} 

Как указано в комментариях ниже, Initialise должны быть государственные или статические (только в C#), он может оставаться защищенным в C++. В C++ вы можете сделать Init приватным и получить доступ к нему только по звонкам до Initialise. См. Не виртуальный интерфейс http://en.wikipedia.org/wiki/Non-virtual_interface_pattern

+1

Как насчет того, чтобы использовать метод 'Init'' public abstract? Хотя, вся концепция наличия метода, который ваши производные классы * должны вызывать в некоторой точке *, неверна. – Groo

+0

+1 для шаблона не виртуального интерфейса. Это действительно полезно в некоторых случаях. – Xaqq

+0

@Groo Making Init public позволит другим классам получить к нему доступ (побеждая цель не виртуального интерфейса). Но вы правы, я не вижу никакой пользы от функции Init над конструктором. –

2

Вы не можете. Защищенный член другого объекта может быть вызван только в том случае, если компилятор может видеть, что объект, о котором идет речь, имеет тот же тип, что и ваш текущий объект. В принципе, «защищенный» означает «Производные классы могут использовать этот член в своем классе».

Основная проблема заключается в том, что вы хотите, чтобы некоторые привилегированные классы («композиты») могли вызывать метод иностранных классов (« компоненты "), объявленные базовым классом, предназначены только для использования производных классов в их собственной реализации.

Возможно, вы захотите сделать Init внутренним, если все композиты находятся в одном пакете. Или, возможно, создать подкласс компонента, который все наборы композиций наследуют и делают этот класс привилегированным для вызова Init на всех компонентах. В C++ вы делали бы такую ​​вещь с объявлениями друзей. В C# тщательное использование внутреннего доступа, вероятно, является правильным решением.

Смежные вопросы