Я сделал некоторые исследования по этому вопросу, и играл с TAspectWeaver
demo от DSharp project для достижения этой цели:
unit Aspects.ChangeDetection;
interface
uses
DSharp.Aspects,
Rtti,
SysUtils,
StrUtils;
type
TChangeDetectionAspect = class(TAspect)
private
class var IsChanged : Boolean;
public
class procedure DoAfter(Instance: TObject; Method: TRttiMethod;
const Args: TArray<TValue>; var Result: TValue); override;
class procedure DoBefore(Instance: TObject; Method: TRttiMethod;
const Args: TArray<TValue>; out DoInvoke: Boolean;
out Result: TValue); override;
class procedure DoException(Instance: TObject; Method: TRttiMethod;
const Args: TArray<TValue>; out RaiseException: Boolean;
Exception: Exception; out Result: TValue); override;
end;
ChangeDetectionAttribute = class(AspectAttribute)
public
constructor Create;
end;
[ChangeDetection]
IChangeable = interface
['{59992EB4-62EB-4A9A-8216-1B14393B003B}']
function GetChanged: Boolean;
procedure SetChanged(const Value: Boolean);
property Changed : boolean read GetChanged write SetChanged;
end;
TChangeable = class(TInterfacedObject, IChangeable)
private
FChanged : Boolean;
function GetChanged: Boolean;
procedure SetChanged(const Value: Boolean);
public
property Changed : boolean read GetChanged write SetChanged;
end;
implementation
{ TChangeDetectionAspect }
class procedure TChangeDetectionAspect.DoAfter(Instance: TObject; Method: TRttiMethod;
const Args: TArray<TValue>; var Result: TValue);
var ic : IChangeable;
begin
if Supports(Instance, IChangeable, ic) then
ic.Changed := IsChanged;
end;
class procedure TChangeDetectionAspect.DoBefore(Instance: TObject; Method: TRttiMethod;
const Args: TArray<TValue>; out DoInvoke: Boolean; out Result: TValue);
var ctx : TRttiContext;
typ : TRttiType;
meth : TRttiMethod;
Res : TValue;
begin
IsChanged := False;
if StartsText('set', Method.Name) then
begin
ctx := TRttiContext.Create;
typ := ctx.GetType(Instance.ClassType);
// call Getxxx counterpart
meth := typ.GetMethod('G'+ Copy(Method.Name, 2, Maxint));
if Assigned(meth) then
try
Res := meth.Invoke(Instance, []);
IsChanged := Res.AsVariant <> Args[0].AsVariant;
except
end;
end;
end;
class procedure TChangeDetectionAspect.DoException(Instance: TObject; Method: TRttiMethod;
const Args: TArray<TValue>; out RaiseException: Boolean; Exception: Exception;
out Result: TValue);
begin
end;
{ ChangeDetectionAttribute }
constructor ChangeDetectionAttribute.Create;
begin
inherited Create(TChangeDetectionAspect);
end;
{ TChangeable }
function TChangeable.GetChanged: Boolean;
begin
Result := FChanged;
end;
procedure TChangeable.SetChanged(const Value: Boolean);
begin
FChanged := Value;
end;
end.
Использование:
unit u_frm_main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Aspects.ChangeDetection, DSharp.Aspects.Weaver;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
IMyObject = interface(IChangeable)
function GetName: String;
procedure SetName(const Value: String);
property Name : String read GetName write SetName;
end;
TMyObject = class(TChangeable, IMyObject)
private
FName : String;
public
function GetName: String;
procedure SetName(const Value: String); virtual;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TMyObject }
function TMyObject.GetName: String;
begin
Result := FName;
end;
procedure TMyObject.SetName(const Value: String);
begin
FName := Value;
end;
procedure TForm1.FormCreate(Sender: TObject);
var MyObject : IMyObject;
begin
MyObject := TMyObject.Create;
MyObject.Changed := False;
AspectWeaver.AddAspect(TMyObject, TChangeDetectionAspect, '^Set');
MyObject.Name := 'yee';
if MyObject.Changed then
ShowMessage('yep changed');
MyObject.Name := 'yee';
if MyObject.Changed then
ShowMessage('oops, not changed should not display');
MyObject.Name := 'yeea';
if MyObject.Changed then
ShowMessage('yep changed');
end;
end.
Пожалуйста, обратите внимание, что вы должны иметь в наименее Delphi2010 для этого.
Я предпочитаю ответ Уоррен, хотя (меньше магии), я просто хотел показать, что это возможно (с прокси виртуальных функций)
Что вы просите не совсем понятно. Вы ищете способ почувствовать, когда свойства объекта были изменены, не прибегая к инициированию событий для каждого объекта getter? –
Обратите внимание, что ваш пример не ясен, так как 'changed' является временным свойством: что он должен возвращать после второго' c.changed'? Должен ли он быть сброшен на false, предполагая, что только один клиент проверяет наличие изменений или должен ли он оставаться истинным? Кроме того, готовы ли вы изменить «TMyClass» или вы хотите ощущать изменения снаружи (что невозможно)? – jpfollenius
Пожалуйста, уточните версию Delphi (добавьте соответствующий конкретный тег)! – menjaraz