我有以下问题:我有一个实体类'TEntity'和一个网格类'TMesh',TEntity需要知道它的元素(TMesh)何时被删除。有没有可行的工作方式我可以从TMesh析构函数中调用TEntity方法'OnMeshRemove'?
//uTEntity
interface
uses
uTMesh;
type
TEntity = class
Mesh : TMesh;
constructor Create(); overload;
procedure OnMeshRemove();
end;
implementation
constructor TEntity.Create();
begin
Mesh := TMesh.Create();
Mesh.EntityContainer := @self;
end;
procedure TEntity.OnMeshRemove();
begin
//Do stuff
end;
end.
//uTMesh
interface
type
TMesh = class
EntityContainer : Pointer;
destructor Remove();
end;
implementation
uses
uTEntity;
destructor TMesh.Remove();
var
PEntity : ^TEntity;
begin
PEntity := EntityContainer;
if Assigned( PEntity^ ) then
begin
PEntity^.OnMeshRemove();
end;
inherited Destroy();
end;
示例:
var
Ent : TEntity;
begin
Ent := TEntity.Create();
Ent.Mesh.Remove();
//I want Ent.OnMeshRemove to be called. In my example code, there is a pointer problem. I need to solve that. Thanks!
end;
PS:我不想拥有像TEntity.RemoveMesh();
这样的TEntity程序答案 0 :(得分:3)
您的TEntity实例应该向TMesh实例注册自己,以便在释放TMesh实例时,它将修改关于它的TEntity实例。
如果您的类是从TComponent类派生的,那么这个机制已经为您实现了;每个TComponent实例都有一个名为FreeNotification的方法和一个名为Notification的方法。任何TComponent实例都可以向调用其FreeNotification方法的其他组件注册自己,并将自身作为参数传递。每当组件被销毁时,它将检查为其免费通知注册的组件列表,并将调用每个已注册组件的Notification方法。这样,只要其他组件即将被销毁,就会通知寄存器组件。
如果一个TComponent实例是另一个TComponent实例的所有者(在您的情况下,TEntity可以是TMesh实例的所有者),那么每当删除TMesh实例时,它都会自动通知。您需要做的就是覆盖其Notification方法并在该方法中执行您想要执行的任何操作。
如果您不想从TComponent类派生类,或者出于任何原因不想使用Delphi的实现,您可以在自己的类中实现相同的行为。您需要一个TMesh类中的内部列表,其中包含应该通知的类列表。您还需要一个方法来向您的TMesh类注册一个类,并且最终您需要一个TEntity类中的方法,只要它被释放就应该被TMesh调用。
这是一个简单的源代码,仅用于演示一般概念。请注意,此示例代码不是线程安全的,可能缺少其他一些检查。我只是快速写下来向您展示如何实现这样一个想法:
unit Unit1;
interface
uses
Classes, Generics.Collections;
type
TBaseClass = class
private
FNotificationList : TList<TBaseClass>;
protected
procedure Notify(AClass: TBaseClass); virtual;
public
procedure RegisterForNotification(AClass: TBaseClass);
procedure UnregisterNotification(AClass: TBaseClass);
constructor Create;
destructor Destroy; override;
end;
TMesh = class(TBaseClass)
end;
TEntity = class(TBaseClass)
private
FMesh : TMesh;
FOnMeshRemoved : TNotifyEvent;
procedure SetMesh(Value: TMesh);
protected
procedure Notify(AClass: TBaseClass); override;
procedure DoMeshRemoved; virtual;
public
constructor Create;
destructor Destroy; override;
property Mesh : TMesh read FMesh write SetMesh;
property OnMeshRemoved : TNotifyEvent read FOnMeshRemoved write FOnMeshRemoved;
end;
implementation
{ TBaseClass }
constructor TBaseClass.Create;
begin
inherited;
FNotificationList := TList<TBaseClass>.Create;
end;
destructor TBaseClass.Destroy;
var
AClass: TBaseClass;
begin
if Assigned(FNotificationList) then
begin
if (FNotificationList.Count > 0) then
for AClass in FNotificationList do
AClass.Notify(Self);
FNotificationList.Free;
FNotificationList := nil;
end;
inherited;
end;
procedure TBaseClass.Notify(AClass: TBaseClass);
begin
end;
procedure TBaseClass.RegisterForNotification(AClass: TBaseClass);
begin
if not Assigned(AClass) then
Exit;
if FNotificationList.IndexOf(AClass) < 0 then
FNotificationList.Add(AClass);
end;
procedure TBaseClass.UnregisterNotification(AClass: TBaseClass);
begin
if not Assigned(AClass) then
Exit;
if FNotificationList.IndexOf(AClass) >= 0 then
FNotificationList.Remove(AClass);
end;
{ TEntity }
constructor TEntity.Create;
begin
inherited;
end;
destructor TEntity.Destroy;
begin
if Assigned(FMesh) then
FMesh.UnregisterNotification(Self);
inherited;
end;
procedure TEntity.DoMeshRemoved;
begin
if Assigned(FOnMeshRemoved) then
FOnMeshRemoved(Self);
end;
procedure TEntity.Notify(AClass: TBaseClass);
begin
inherited;
FMesh := nil;
DoMeshRemoved;
end;
procedure TEntity.SetMesh(Value: TMesh);
begin
if Assigned(FMesh) then
begin
FMesh.UnregisterNotification(Self);
FMesh := nil;
end;
if Assigned(Value) then
begin
FMesh := Value;
FMesh.RegisterForNotification(Self);
end;
end;
end.
在此代码中,TEntity和TMesh都是从提供通知机制的TBaseClass派生的。 TEntity最初不会创建任何TMesh实例,但您可以将创建的TMesh实例分配给其Mesh属性。这样做会使TEntity将该值赋给其FMesh字段,并调用其RegisterForNotification类,以便在网格被破坏时通知它。
当网格被破坏时,它会遍历所有使用它注册的对象,并调用它们的Notify方法。这里是TEntity类的Notify方法。在TEntity的Notify方法中,它首先使其FMesh字段为nil,因为该对象正在被销毁,并且不需要保留死对象的引用。然后它调用DoMeshRemove方法,该方法是OnMeshRemove事件的事件调用者。
答案 1 :(得分:3)
Delphi中的所有对象都是指针类型,因此无需遵循它。这里有点简单
type
TEntity = class
public
Mesh: TMesh;
constructor Create;
destructor Destroy; override;
end;
implementation
constructor TEntity.Create;
begin
inherited Create;
Mesh := TMesh.Create;
Mesh.EntityContainer := Self;
end;
procedure TEntity.Destroy;
begin
if Mesh <> nil then
begin
Mesh.EntityContainer := nil;
FreeAndNil(Mesh);
end;
inherited Destroy;
end;
//***************************************************
type
TMesh = class
EntityContainer: TObject;
destructor Destroy; override;
end;
implementation
uses
uTEntity;
destructor TMesh.Destroy;
begin
if (EntityContainer <> nil) and (TEntity(EntityContainer).Mesh = Self) then
TEntity(EntityContainer).Mesh := nil;
EntityContainer := nil;
inherited Destroy;
end;
答案 2 :(得分:2)
修改:回到PC后面。
维护某个班级的预定对象列表的经典方法是使用TCollection / TCollectionItem。
在Delphi中大量使用TCollection / TCollectionItem(参见this list)。 它们的重量轻于TComponent(自动维护Owner / Components / ComponentCount并且FreeNotification},因为TCollectionItem和TCollection都来自{{3而不是the TPersistent branch TCollection有一个很好的虚拟TComponent branch方法:
procedure TCollection.Notify(Item: TCollectionItem; Action: TCollectionNotification);
begin
case Action of
cnAdded: Added(Item);
cnDeleting: Deleting(Item);
end;
end;
从Delphi 2009开始,你有了泛型,所以它可以节省大量的时间来使用Notify(在你的演员TList<TEntity>
中,因为它包含了这个非常好的TList方法和{ {3}}事件:
procedure TList<T>.Notify(const Item: T; Action: TCollectionNotification);
begin
if Assigned(FOnNotify) then
FOnNotify(Self, Item, Action);
end;
如果TMesh
确实是TEntity
的集合/列表,这两个解决方案效果很好
如果TMesh
是TEntity
的非列表图表,那么最好从Notify下降,例如OnNotify中解释的vcldeveloper TComponent。
link text有一篇关于使用his answer的各种方式的精彩博文,包括Andy Bulka的均衡视图。
- 的Jeroen
旧答案(很好的iPad自动完成错误修复):
对不起,答案很简短,因为我在旅途中只携带移动设备。
看起来你的网格是实体的容器 如果是这样,那么你应该研究TCollection和TCollectionItem 从前者和你的实体派生你的网格。
delphi vcl源代码包含许多这样的示例:fields / field或actions / action是很好的启动器。