按地址引用另一个类中的类

时间:2010-12-25 13:16:58

标签: delphi class pointers methods

我有以下问题:我有一个实体类'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程序

3 个答案:

答案 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的集合/列表,这两个解决方案效果很好 如果TMeshTEntity的非列表图表,那么最好从Notify下降,例如OnNotify中解释的vcldeveloper TComponent

link text有一篇关于使用his answer的各种方式的精彩博文,包括Andy Bulka的均衡视图。

- 的Jeroen

旧答案(很好的iPad自动完成错误修复):

对不起,答案很简短,因为我在旅途中只携带移动设备。

看起来你的网格是实体的容器 如果是这样,那么你应该研究TCollection和TCollectionItem 从前者和你的实体派生你的网格。

delphi vcl源代码包含许多这样的示例:fields / field或actions / action是很好的启动器。