我做了關於這個問題的一些研究,並與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觸發事件? – 2012-03-30 17:21:48
請注意,你的例子並不清楚,因爲'changed'是一個時態屬性:第二個'c.changed'後應該返回什麼?假如只有一個客戶檢查變化或者它是否保持真實,它是否應該重置爲假?另外,你是否願意改變'TMyClass',或者你想從外部感知變化(這是不可能的)? – jpfollenius 2012-03-30 17:25:46
請確定您的Delphi版本(添加適當的特定標籤)! – menjaraz 2012-03-31 08:45:32