Веселье с кастингом и наследством - PullRequest
3 голосов
/ 26 мая 2010

ПРИМЕЧАНИЕ. Этот вопрос написан на псевдокоде на языке C #, но я действительно собираюсь спросить, на каких языках есть решение. Пожалуйста, не зацикливайтесь на синтаксисе.

Скажем, у меня есть два класса:

 class AngleLabel: CustomLabel
 {
     public bool Bold;  // Just upping the visibility to public
     // code to allow the label to be on an angle
 }

 class Label: CustomLabel
 {
     public bool Bold;  // Just upping the visibility to public
     // Code for a normal label
     // Maybe has code not in an AngleLabel (align for example).
 }

Они оба происходят от этого класса:

 class CustomLabel
 {
     protected bool Bold;
 }

Поля, выделенные жирным шрифтом, отображаются в открытых классах как открытые.

Нет доступных интерфейсов для классов.

Теперь у меня есть метод, который я хочу передать в CustomLabel и установить свойство Bold. Можно ли это сделать без необходимости: 1) выяснить, каков реальный класс объекта и 2) привести к этому объекту, а затем 3) создать отдельный код для каждой переменной каждого типа метки, чтобы установить жирный шрифт. Вроде как это:

 public void SetBold(customLabel: CustomLabel)
 {
     AngleLabel angleLabel;
     NormalLabel normalLabel;


     if (angleLabel is AngleLabel )
     {
        angleLabel= customLabel as AngleLabel 
        angleLabel.Bold = true;
     }

     if (label is Label)
     {
        normalLabel = customLabel as Label
        normalLabel .Bold = true;
     }
 }

Было бы неплохо сделать один бросок, а затем установить жирный шрифт для одной переменной.

Я думал о том, чтобы создать четвертый класс, который бы просто выставлял переменную bold и использовал свой собственный ярлык для этого класса.

Будет ли это работать?

Если так, на каких языках он будет работать? (Этот пример взят из старой версии Delphi (Delphi 5)). Я не знаю, будет ли это работать для этого языка (мне все еще нужно попробовать), но мне любопытно, будет ли он работать для C ++, C # или Java.

Если нет, какие-нибудь идеи о том, что будет работать? (Помните, что интерфейсы не предоставляются, и я не могу изменять классы.)

У кого-нибудь есть догадки?

Ответы [ 6 ]

7 голосов
/ 26 мая 2010

Это будет работать в Delphi. Код в том же модуле, что и используемые им классы, имеет неявный доступ к защищенным (но не строго защищенным) членам, даже тем членам, которые объявлены в другом модуле. Вы объявляете собственность защищенной в CustomLabel:

type
  CustomLabel = class
  private
    FBold: Boolean;
  protected
    property Bold: Boolean read FBold write FBold;
  end;

Процедура установки жирным шрифтом в другом блоке будет иметь собственный потомок CustomLabel:

type
  TAccessCustomLabel = class(CustomLabel);

procedure SetBold(customLabel: CustomLabel)
begin
  TAccessCustomLabel(customLabel).Bold := True;
end;

Вы не можете использовать as приведение, потому что фактический параметр никогда не будет экземпляром TAccessLabel. Это будет экземпляр AngleLabel или NormalLabel, но поскольку части, унаследованные от CustomLabel всеми тремя классами, являются общими, свойство Bold одинаково во всех них. Это остается верным даже после того, как свойство было опубликовано или опубликовано в потомке:

type
  AngleLabel = class(CustomLabel)
  public
    property Bold;
  end;

Вы можете изменить видимость свойств, но не полей. Если вы попробуете то же самое с полем, вы объявите новое поле с тем же именем, которое скрывает унаследованное поле.


Вы можете сделать что-то подобное в C ++, но это не так часто, как в Delphi, так что, скорее всего, это вызовет ярость, особенно если вы собираетесь писать переносимый код.

Объявите четвертый класс, как в Delphi. C ++ не так свободен в доступе к членам, как Delphi, но в нем есть концепция дружба , которая в этом случае работает так же хорошо.

class AccessCustomLabel: public CustomLabel
{
  friend void SetLabel(CustomLabel* customLabel);
};

Эта функция теперь имеет полный доступ к членам класса:

void SetLabel(CustomLabel* customLabel)
{
  // Not allowed:
  // customLabel->bold = true

  // Not ordinarily allowed; requires friendship
  reinterpret_cast<AccessCustomLabel*>(customLabel)->bold = true;
}

Технически это неопределенное поведение , потому что мы привели тип объекта к типу, которого он на самом деле не имеет. Мы полагаемся на то, что все потомки CustomLabel имеют одинаковую разметку, в частности, чтобы bold член AccessCustomLabel находился в той же относительной позиции, что и bold член любого другого CustomLabel потомка .


Приведение типов в коде Delphi и C ++ выполняет type punning . Это не сойдет с рук в C # или Java; они проверяют результаты своих приведений, поэтому, если customLabel на самом деле не содержит экземпляр AccessCustomLabel, вы получите исключение. Вы должны будете использовать отражение, чтобы получить доступ к защищенным членам не связанных классов на этих языках. Демонстрируя это за пределами моей глубины.

3 голосов
/ 26 мая 2010

Поскольку родитель защищен, ответ таков: он не должен работать на каком-либо языке без дополнительной работы, если только соответствующий код не выполняется в потомке пользовательской версии.

Delphi имеет возможность использовать защищенный хак , чтобы заставить его работать. C # и Java могут использовать отражение. Друзья C ++ могут быть использованы, чтобы заставить его работать.

Но если бы вы объявили Bold как Public в CustomLabel, эта функциональность работала бы на всех указанных вами языках. Delphi, C ++, C # и Java без необходимости делать что-то особенное.

1 голос
/ 26 мая 2010

В C # 3.0 и выше вы можете использовать методы расширения ; они аналогичны помощникам класса Delphi , о которых Джерри упоминал.

Идет по этим направлениям (посмотрите ключевое слово this).

public static class CustomLabelExtensions // name here is not important, just make it readable
{
    public static void SetBolded(this CustomLabel customLabel, bool newValue)
    {
        customLabel.Bold = newValue;
    }
}   

Обратите внимание, что я пропустил пространство имен, как вы сделали в своем псевдокоде.
Однако убедитесь, что CustomLabelExtensions видны вашему коду, либо используя его пространство имен, либо явно указав это пространство имен.

Также обратите внимание, что методы расширения позволяют добавлять только методы, а не свойства (исходя из фона Delphi, что было странно для меня).

Затем вы используете приведенный выше код следующим образом:

AngleLabel angleLabel;
NormalLabel normalLabel;
// some code that assigns values to the variables
angleLabel.SetBolded(true);
normalLabel.SetBolded(true);

- Йерун

1 голос
/ 26 мая 2010

Если вы используете Delphi 2005 или более позднюю версию, вы можете использовать помощники классов. Помощники класса могут получить доступ к защищенным полям и методам класса «помогли». Нечто подобное может быть возможно с методами расширения C #, но это не та область, с которой я хорошо знаком.

Примечание: я не смог проверить это, так как у меня нет под рукой компилятора.

type
   TLabelHelper = class helper for CustomLabel
   public 
     procedure SetBolded(ABold : Boolean);
   end;

procedure TLabelHelper.SetBolded(ABold : Boolean);
begin
  Bold := ABold;
end;

...

Label.SetBolded(True);
1 голос
/ 26 мая 2010

C ++ решит это с помощью шаблона (при условии, что SetBold эквивалентен Bold в вашем примере):

template<typename T> void SetBold(T t) {
    t.SetBold();
}
0 голосов
/ 26 мая 2010

Что вы можете сделать, это добавить промежуточный класс (названный, например, CCC) между CustomLabel и двумя другими классами.

public class CCC : CustomLabel

Этот класс должен иметь функцию с именем SetBold (bool bold), которая будет устанавливать защищенное поле.

public void SetBold(bool bold)
{
    base.Bold = bold;
}

AngleLabel и Label оба наследуют CCC

и параметр для SetBold (...) будет иметь тип CCC

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...