Ссылка на класс внутри другого класса по адресу - PullRequest
0 голосов
/ 25 декабря 2010

У меня следующая проблема: у меня есть класс сущности 'TEntity' и класс сетки 'TMesh', и TEntity необходимо знать, когда удаляется его элемент (TMesh).Есть ли возможный рабочий способ, которым я могу вызвать метод TEntity OnMeshRemove из деструктора TMesh?

//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, такую ​​как TEntity.RemoveMesh ();

Ответы [ 3 ]

3 голосов
/ 25 декабря 2010

Все объекты в 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;
3 голосов
/ 25 декабря 2010

Ваш экземпляр TEntity должен зарегистрироваться в экземпляре TMesh, чтобы при освобождении экземпляра TMesh он изменил экземпляр TEntity.

Если ваши классы являются производными от класса TComponent, то этот механизм уже реализован для вас; каждый экземпляр TComponent имеет метод FreeNotification и метод Notification. Любой экземпляр TComponent может зарегистрироваться в другом компоненте, вызывая его метод FreeNotification и передавая себя в качестве параметра. Всякий раз, когда компонент уничтожается, он проверяет список компонентов, зарегистрированных для его бесплатного уведомления, и вызывает метод уведомления каждого зарегистрированного компонента. Таким образом, компонент регистра будет уведомлен всякий раз, когда другой компонент будет уничтожен.

Если один экземпляр TComponent является владельцем другого (в вашем случае TEntity может быть владельцем экземпляра TMesh), то он будет уведомляться автоматически при каждом удалении экземпляра TMesh. Все, что вам нужно сделать, это переопределить его метод уведомления и делать все, что вы хотите сделать внутри этого метода.

Если вы не хотите выводить ваши классы из класса 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. Здесь это будет метод Notify класса TEntity. Внутри метода Notify объекта TEntity он сначала устанавливает в своем поле FMesh значение nil, поскольку этот объект уничтожается и нет необходимости сохранять ссылку на мертвый объект. Затем он вызывает метод DoMeshRemove, который вызывает события для события OnMeshRemove.

2 голосов
/ 26 декабря 2010

Редактировать : позади ПК.

Классический способ ведения списков предопределенных объектов определенного класса - это TCollection / TCollectionItem .

TCollection / TCollectionItem широко используются в Delphi (см. этот список ). Они имеют меньший вес, чем TComponent (который автоматически поддерживает Владелец / Компоненты / ComponentCount и имеет FreeNotification ), поскольку TCollectionItem и TCollection оба происходят от TPersistent ветви вместо TComponent ответвления .
У TCollection есть хороший виртуальный метод уведомления :

procedure TCollection.Notify(Item: TCollectionItem; Action: TCollectionNotification);
begin
  case Action of
    cnAdded: Added(Item);
    cnDeleting: Deleting(Item);
  end;
end;

Начиная с Delphi 2009, у вас есть дженерики, поэтому он может окупиться за использование TList (в вашем приведении TList<TEntity>, так как он содержит очень хороший метод Notify и OnNotify событие:

procedure TList<T>.Notify(const Item: T; Action: TCollectionNotification);
begin
  if Assigned(FOnNotify) then
    FOnNotify(Self, Item, Action);
end;

Эти два решения хорошо работают, если ваш TMesh действительно является коллекцией / списком TEntity.
Если это TMesh является графиком не из списка TEntity, то лучше использовать как TComponent как vcldeveloper текст ссылки объясненный в его ответ .

Энди Булька имеет хороший пост в блоге о различных способах использования списков и коллекций , включая хорошо сбалансированное представление TCollection / TCollectionItem .

- Йерун

Старый ответ (исправлены большие ошибки автозаполнения iPad):

Извините за краткий ответ, так как я нахожусь в дороге только с мобильным устройством.

Похоже, ваша сетка является контейнером для сущностей.
Если это так, то вам следует изучить TCollection и TCollectionItem.
Извлеките свою сетку из первой, а свою сущность из последней.

Исходный код Delphi vcl содержит множество примеров: поля / поля или действия / действия - хорошие стартеры.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...