Pull to refresh

Comments 12

А всё-таки, почему не написать свой компонент? Ведь вы уже все сделали, осталось только собрать в пакет и инсталлировать.

Я постараюсь подробно ответить в ближайшее время в статье. Там будет много спорного материала, но это все равно опыт, который мне помогает.
Довольно интересный подход с подменой компонента) В принципе, никаких чудес, но я даже не задумывался, что «так можно»)
Еще и не так можно. Все расскажу, дайте время. На самом деле все просто, если смотреть под иным углом зрения.
Как-то я имел дело с оперднем, в котором в зависимости от бизнес-логики у объектов в рантайме подменялась таблица виртуальных методов (VMT). Легким движением руки элементарные проводки становились выданными кредитами, лицевые счета — договорами и т.д. Глючило оно безбожно, пока не переписал на нормальное присвоение.

Я к тому, что иной угол зрения имеет право на жизнь, но не стоит увлекаться. В вашем примере вы имеете полный контроль над формой, а если форма чужая, то указатель на оригинальный TMemo мог быть где-то сохранен и использован позднее, а сам объект вы уже уничтожили.
Не понял — в чем смысл использовать TMemo, а потом через одно место его подменять? Почему сразу не использовать ваш дочерний класс?
Есть более простой и менее опасный (в плане возможности наделать ошибок) способ «подмены». Хотя это уже совсем не подмена.

type
  TMemo = class(StdCtrls.TMemo)
    ...
  end;


Единственное требование — такой класс должен быть объявлен перед объявлением класса формы. По идее, будет работать, если объявить в отдельном модуле, но в uses этот модуль должен быть указан обязательно после StdCtrls.

В результате в design-time ничего не меняется — там остается стандартный TMemo, а в run-time имеем доступ ко всем новым «фичам» нового класса.

Повторюсь — подмены тут никакой нет, компонент создается сразу с нужной функциональностью. Не надо создавать экземпляр нового класса, не надо ничего копировать. Отпадает необходимость писать метод TxIPMemo.SetMemo.
В результате в design-time ничего не меняется — там остается стандартный TMemo, а в run-time имеем доступ ко всем новым «фичам» нового класса.

Зачем?
Почему просто не сделать свой компонент и размещать на форме его?
Вы абсолютно правы. Это красивое и элегантное решение, чтобы подменить в run-time все компоненты, созданные в дизайне, на свои. Только не всегда нужно заменять абсолютно все.

SetMemo имеет два параметра: указатель на TCustomMemo и AWithFree – уничтожать ли. Т.е. передать можно любого наследника TCustomMemo, не обязательно TMemo, компонент возьмет все необходимые ему свойства. И можно не уничтожать «донора», пусть живет как есть.

Вот, кстати, на кой чёрт там указатель?
var параметры придумали миллиард лет назад. Ещё в Borland Pascal были.

Можно попробовать сделать такую запись:
function F1 (var m : TCustomMemo) : boolean;

Далее в коде вызвать хотя бы даже с «родным» наследником Memo1: TMemo
F1 (Memo1);

Компилятор будет не доволен.
Точно, вы правы.
Тогда можно сделать такой финт ушами:

{$apptype console}
program Test;

uses
  System.SysUtils;

type
  TBase = class(TObject)
  end;

  TFoo = class(TBase)
  end;

  TBar = class(TBase)
  end;

  TXyz = class(TObject)
  end;

  TUtility = class(TObject)
  public
    class procedure SurpriseMe<T: TBase>(var AValue: T);
  end;

class procedure TUtility.SurpriseMe<T>(var AValue: T);
begin
  FreeAndNil(AValue);
  AValue := T(TBar.Create());
end;

var
  ObjFoo: TBase;
  ObjXyz: TXyz;
begin
  
  ObjFoo := TFoo.Create();
  
  try 
    writeln('Before:'#9, ObjFoo.ClassName);
    TUtility.SurpriseMe(ObjFoo);
    writeln('After:'#9, ObjFoo.ClassName);
  finally
    FreeAndNil(ObjFoo);
  end;
  
  // Will fail to compile at SurpriseMe() call.
  {
  ObjXyz := TXyz.Create();
  
  try 
    writeln('Before:'#9, ObjXyz.ClassName);
    TUtility.SurpriseMe(ObjXyz);
    writeln('After:'#9, ObjXyz.ClassName);
  finally
    FreeAndNil(ObjXyz);
  end;
  }
end.

Как минимум в Delphi XE8 должно уже работать.

Тут, как оказалось, ещё и проверка типов переменной ломается.
Sign up to leave a comment.

Articles