如何避免循环单元引用?

时间:2023-01-13 12:12:30

Imagine the following two classes of a chess game:

想象一下以下两类棋类游戏:

TChessBoard = class
private
  FBoard : array [1..8, 1..8] of TChessPiece;
...
end;

TChessPiece = class abstract
public
   procedure GetMoveTargets (BoardPos : TPoint; Board : TChessBoard; MoveTargetList : TList <TPoint>);
...
end;

I want the two classes to be defined in two separate units ChessBoard.pas and ChessPiece.pas.

我希望在两个独立的单元棋盘中定义这两个类。不是和ChessPiece.pas。

How can I avoid the circular unit reference that I run into here (each unit is needed in the other unit's interface section)?

如何避免在这里遇到的循环单元引用(另一个单元的接口部分需要每个单元)?

9 个解决方案

#1


11  

Change the unit that defines TChessPiece to look like the following:

将定义TChessPiece的单元设置为如下所示:

TYPE
  tBaseChessBoard = class;

  TChessPiece = class
    procedure GetMoveTargets (BoardPos : TPoint; Board : TBaseChessBoard; ...    
  ...
  end;    

then modify the unit that defines TChessBoard to look like the following:

然后修改定义TChessBoard的单元,使其看起来如下所示:

USES
  unit_containing_tBaseChessboard;

TYPE
  TChessBoard = class(tBaseChessBoard)
  private
    FBoard : array [1..8, 1..8] of TChessPiece;
  ...
  end;  

This allows you to pass concrete instances to the chess piece without having to worry about a circular reference. Since the board uses the Tchesspieces in its private, it really doesn't have to exist prior to the Tchesspiece declaration, just as place holder. Any state variables which the tChessPiece must know about of course should be placed in the tBaseChessBoard, where they will be available to both.

这允许您将具体实例传递给棋子,而不必担心循环引用。由于董事会在私底下使用了Tchesspieces,所以在Tchesspiece申报之前,它实际上并不一定要存在,就像权利人一样。当然,tChessPiece必须知道的任何状态变量都应该放在tBaseChessBoard中,在那里,这两个变量都可以使用。

#2


22  

Delphi units are not "fundamentally broken". The way they work facilitates the phenomenal speed of the compiler and promotes clean class designs.

德尔福的单位并不是“从根本上破损的”。它们的工作方式促进了编译器的惊人速度,并促进了干净的类设计。

Being able to spread classes over units in the way that Prims/.NET allows is the approach that is arguably fundamentally broken as it promotes chaotic organisation of classes by allowing the developer to ignore the need to properly design their framework, promoting the imposition of arbitrary code structure rules such as "one class per unit", which has no technical or organisation merit as a universal dictum.

能够以Prims/的方式将类分散到单元上。净允许的方法可以说是已经支离破碎,因为它促进了混乱的组织类通过允许开发人员忽略了需要恰当地设计框架,促进任意代码结构的实施规则,如“一级单位”,没有技术或组织绩效作为一个普遍的格言。

In this case, I immediately noticed an idiosynchracy in the class design arising from this circular reference dilemma.

在这种情况下,我立刻注意到,在这个循环引用的两难境地下,课堂设计出现了一个白痴式的同步。

That is, why would a piece ever have any need to reference a board?

也就是说,为什么一件作品有必要引用一块板呢?

If a piece is taken from a board, such a reference then makes no sense, or perhaps the valid "MoveTargets" for a removed piece are only those valid for that piece as a "starting position" in a new game? But I don't think this makes sense as anything other than a arbitrary justification for a case that demands that GetMoveTargets support invocation with a NIL board reference.

如果一块棋子是从棋盘上取下来的,那么这样的引用就没有意义了,或者,对于被移除的棋子来说,有效的“MoveTargets”仅仅是那些在新游戏中作为“起始位置”的棋子有效吗?但我不认为这是有意义的,因为这是一个任意的理由,要求GetMoveTargets支持使用NIL板引用的调用。

The particular placement of an individual piece at any given time is a property of an individual game of chess, and equally the VALID moves that may be POSSIBLE for any given piece are dependent upon the placement of OTHER pieces in the game.

一个棋子在任何给定时间的特定位置是棋子的一个属性,同样,任何棋子可能的有效移动也取决于游戏中其他棋子的位置。

TChessPiece.GetMoveTargets does not need knowledge of the current game state. This is the responsibility of a TChessGame. And a TChessPiece does not need a reference to a game or to a board to determine the valid move targets from a given current position. The board constraints (8 ranks and files) are domain constants, not properties of a given board instance.

TChessPiece。GetMoveTargets不需要了解当前游戏状态。这是TChessGame的责任。而且,TChessPiece不需要参考游戏或棋盘来确定当前位置的有效移动目标。板约束(8个等级和文件)是域常量,而不是给定板实例的属性。

So, a TChessGame is required that encapsulates the knowledge that combines awareness of a board, the pieces and - crucially - the rules, but the board and the pieces do not need knowledge of each other OR of the game.

因此,需要一个TChessGame来封装对棋盘、棋子以及——关键是——规则的认识,但棋盘和棋子不需要相互了解或了解游戏。

It may seem tempting to put the rules pertaining to different pieces in the class for the piece type itself, but this is a mistake imho, since many of the rules are based on interactions with other pieces and in some cases with specific piece types. Such "big picture" behaviours require a degree of over-sight (read: overview) of the total game state that is not appropriate in a specific piece class.

在类中为片段类型本身设置与不同片段相关的规则似乎很有吸引力,但这在imho中是一个错误,因为许多规则都是基于与其他片段的交互,在某些情况下还基于特定的片段类型。这样的“大图景”行为需要对整个游戏状态进行一定程度的过度关注(参见:概述),这在特定的片类中是不合适的。

e.g. a TChessPawn may determine that a valid move target is one or two squares forward or one square diagonally forward if either of those diagonal squares are occupied. However, if the movement of the pawn exposes the King to a CHECK situation, then the pawn is not movable at all.

例如,如果一个TChessPawn占用了这些对角线上的方块,那么它就可以确定一个有效的移动目标是向前移动的一个或两个方块,还是向斜前方移动一个正方形。但是,如果典当的移动将国王暴露在检查的情况下,那么典当根本就不能移动。

I would approach this by simply allowing the pawn class to indicate all POSSIBLE move targets - 1 or 2 squares forward and both diagonally forward squares. The TChessGame then determines which of these is valid by reference to occupancy of those move targets and game state. 2 squares forward is only possible if the pawn is on it's home rank, forward squares being occupied BLOCK a move = invalid target, UNoccupied diagonal squares FACILITATE a move, and if any otherwise valid move exposes the King, then that move is also invalid.

我可以通过简单地让典当类指示所有可能的移动目标来实现这一点——1或2个向前的方块和两个对角线向前的方块。然后,TChessGame通过引用移动目标的占用率和游戏状态来确定哪些是有效的。只有当兵在本垒上时,才有可能向前移动2个方块,向前移动的方块被占据a块移动=无效目标,未被占用的对角线方块会促进移动,如果其他有效的移动暴露了国王,那么移动也无效。

Again, the temptation might be to put the generally applicable rules in the base TChessPiece class (e.g. does a given move expose the King?), but applying that rule requires awareness of the overall game state - i.e. placement of other pieces - so it more properly belongs as a generalised behaviour of the TChessGame class, imho

的诱惑可能将普遍适用的规则TChessPiece基类(例如给定的举动让国王吗?),但应用该规则需要整体游戏状态的意识——即位置的其他部分——因此更恰当的属于TChessGame类的普遍行为,恕我直言

In addition to move targets, pieces also need to indicate CaptureTargets, which in the case of most pieces is the same, but in some cases quite different - pawn being a good example. But again, which - if any - of all potential captures is effective for any given move is - imho - an assessment of the game rules, not the behaviour of a piece or class of pieces.

除了移动目标之外,棋子还需要指示CaptureTargets,在大多数情况下,这是相同的,但是在某些情况下是完全不同的——典当就是一个很好的例子。但同样,如果有的话,所有可能的捕捉对任何给定的动作都是有效的——imho——是对游戏规则的评估,而不是对一段或一组棋子的行为的评估。

As is the case in 99% of such situations (ime - ymmv) the dilemma is perhaps better solved by changing the class design to better represent the problem being modelled, not finding a way to shoehorn the class design into an arbitrary file organisation.

就像99%的这种情况(ime - ymmv)一样,通过改变类的设计来更好地代表正在建模的问题,而不是找到将类设计塞进任意文件组织的方法,这可能会更好地解决这个难题。

#3


16  

One solution could be the introduction of a third unit which contains interface declarations (IBoard and IPiece).

一种解决方案是引入第三个单元,其中包含接口声明(IBoard和IPiece)。

Then the interface sections of the two units with class declarations can refer to the other class by its interface:

然后两个具有类声明的单元的接口部分可以通过其接口指向另一个类:

TChessBoard = class(TInterfacedObject, IBoard)
private
  FBoard : array [1..8, 1..8] of IPiece;
...
end;

and

TChessPiece = class abstract(TInterfacedObject, IPiece)
public
   procedure GetMoveTargets (BoardPos: TPoint; const Board: IBoard; 
     MoveTargetList: TList <TPoint>);
...
end;

(The const modifier in GetMoveTargets avoids unnecessary reference counting)

(GetMoveTargets中的const修饰符避免不必要的引用计数)

#4


2  

It would be better to move ChessPiece class into ChessBoard unit.
If for some reason you can't, try to put one uses clause to the implementation part in one unit and leave the other one in the interface part.

最好把切斯皮埃斯班搬到棋盘上。如果由于某些原因您不能,试着将一个use子句放在一个单元中的实现部分,而将另一个在接口部分中保留。

#5


1  

With Delphi Prism you can spread your namespaces over separate files, so there you would be able to solve it in a clean way.

使用Delphi Prism,您可以将名称空间分散到不同的文件中,这样您就能够以一种干净的方式解决它。

The way units work are just fundamentally broken with their current Delphi implementation. Just look at how "db.pas" needed to have TField, TDataset, TParam, etc in one monstrous .pas file because their interfaces reference each other.

单元工作的方式与当前的Delphi实现基本没有关系。看看db。pas"需要在一个巨大的。pa文件中包含TField、TDataset、TParam等,因为它们的接口相互引用。

Anyway, you can always move code to a separate file and include them with {$include ChessBoard_impl.inc} for example. That way you can split stuff over files and have separate versions via your vcs. However, it's just a bit unhandy to edit files that way.

无论如何,您总是可以将代码移动到一个单独的文件中,并将其包含为{$include ChessBoard_impl。例如公司}。这样,你就可以在文件上分割文件,并通过风投建立不同的版本。但是,这样编辑文件有点不方便。

The best long-term solution would be to urge embarcadero to ditch some of the ideas that made sense in 1970 when pascal was born, but that are not much more than a pain in the ass for developers nowadays. A one-pass compiler is one of those.

最好的长期解决方案是敦促embarcadero放弃一些在1970年pascal诞生时就有意义的想法,但这对现在的开发人员来说不过是麻烦。一遍编译器就是其中之一。

#6


0  

It does not look like TChessBoard.FBoard needs to be an array of TChessPiece, it can as well be of TObject and be downcasted in ChessPiece.pas.

它看起来不像TChessBoard。FBoard需要一个TChessPiece数组,它也可以是TObject,并在chesspiec .pas中被降级。

#7


0  

Another approach:

另一种方法:

Make your board of tBaseChessPiece. It's abstract but contains the definitions you need to refer to.

做你的板子。它是抽象的,但包含您需要引用的定义。

The internal workings are in tChessPiece which descends from tBaseChessPiece.

内部工作在tChessPiece中,它来自tBaseChessPiece。

I do agree that Delphi's handling of things that refer to each other is bad--about the worst feature of the language. I've long called for forward declarations that work across units. The compiler would have the information it needs, it wouldn't break the one-pass nature that makes it so fast.

我同意德尔菲对相互联系的事物的处理是不好的——这是语言最糟糕的特点。我一直呼吁在单位之间工作的前向声明。编译器会得到它所需要的信息,它不会破坏使它如此快速的单步性。

#8


0  

what about this approach:

这个方法:

chess board unit:

棋盘单元:

TBaseChessPiece = class 

public

   procedure GetMoveTargets (BoardPos : TPoint; Board : TChessBoard; MoveTargetList : TList <TPoint>); virtual; abstract;

...

TChessBoard = class
private
  FBoard : array [1..8, 1..8] of TChessPiece;

  procedure InitializePiecesWithDesiredClass;
...

pieces unit:

件单位:

TYourPiece = class TBaseChessPiece

public 

   procedure GetMoveTargets (BoardPos : TPoint; Board : TChessBoard; MoveTargetList : TList <TPoint>);override;

...

In this aproach chess board unit will include the reference of pieces unit only in implementation section(due to the method that will in fact create the objects) and pieces unit will have a reference to chess board unit in interface. If I'm not wrong this handle your problem in a easy way...

在这个象棋棋盘单元中,棋子单元将只包含在执行部分的引用(由于实际上创建棋子的方法),棋子单元将在接口中引用棋子单元。如果我没弄错的话,这可以很容易地解决你的问题……

#9


0  

Derive TChessBoard from TObject

TChessBoard来自TObject

TChessBoard = class(TObject)

TChessBoard =类(TObject)

then you can declare procedure GetMoveTargets (BoardPos : TPoint; Board : TObject; MoveTargetList : TList );

然后可以声明GetMoveTargets (BoardPos: TPoint;董事会:TObject;MoveTargetList:TList);

when you call the proc, use SELF as the Board object (if you are calling it from there), then you can reference it with

调用proc时,使用SELF作为Board对象(如果是从那里调用),然后可以引用它

(Board as TChessBoard). and access the properties etc from that.

(董事会TChessBoard)。然后访问属性等等。

#1


11  

Change the unit that defines TChessPiece to look like the following:

将定义TChessPiece的单元设置为如下所示:

TYPE
  tBaseChessBoard = class;

  TChessPiece = class
    procedure GetMoveTargets (BoardPos : TPoint; Board : TBaseChessBoard; ...    
  ...
  end;    

then modify the unit that defines TChessBoard to look like the following:

然后修改定义TChessBoard的单元,使其看起来如下所示:

USES
  unit_containing_tBaseChessboard;

TYPE
  TChessBoard = class(tBaseChessBoard)
  private
    FBoard : array [1..8, 1..8] of TChessPiece;
  ...
  end;  

This allows you to pass concrete instances to the chess piece without having to worry about a circular reference. Since the board uses the Tchesspieces in its private, it really doesn't have to exist prior to the Tchesspiece declaration, just as place holder. Any state variables which the tChessPiece must know about of course should be placed in the tBaseChessBoard, where they will be available to both.

这允许您将具体实例传递给棋子,而不必担心循环引用。由于董事会在私底下使用了Tchesspieces,所以在Tchesspiece申报之前,它实际上并不一定要存在,就像权利人一样。当然,tChessPiece必须知道的任何状态变量都应该放在tBaseChessBoard中,在那里,这两个变量都可以使用。

#2


22  

Delphi units are not "fundamentally broken". The way they work facilitates the phenomenal speed of the compiler and promotes clean class designs.

德尔福的单位并不是“从根本上破损的”。它们的工作方式促进了编译器的惊人速度,并促进了干净的类设计。

Being able to spread classes over units in the way that Prims/.NET allows is the approach that is arguably fundamentally broken as it promotes chaotic organisation of classes by allowing the developer to ignore the need to properly design their framework, promoting the imposition of arbitrary code structure rules such as "one class per unit", which has no technical or organisation merit as a universal dictum.

能够以Prims/的方式将类分散到单元上。净允许的方法可以说是已经支离破碎,因为它促进了混乱的组织类通过允许开发人员忽略了需要恰当地设计框架,促进任意代码结构的实施规则,如“一级单位”,没有技术或组织绩效作为一个普遍的格言。

In this case, I immediately noticed an idiosynchracy in the class design arising from this circular reference dilemma.

在这种情况下,我立刻注意到,在这个循环引用的两难境地下,课堂设计出现了一个白痴式的同步。

That is, why would a piece ever have any need to reference a board?

也就是说,为什么一件作品有必要引用一块板呢?

If a piece is taken from a board, such a reference then makes no sense, or perhaps the valid "MoveTargets" for a removed piece are only those valid for that piece as a "starting position" in a new game? But I don't think this makes sense as anything other than a arbitrary justification for a case that demands that GetMoveTargets support invocation with a NIL board reference.

如果一块棋子是从棋盘上取下来的,那么这样的引用就没有意义了,或者,对于被移除的棋子来说,有效的“MoveTargets”仅仅是那些在新游戏中作为“起始位置”的棋子有效吗?但我不认为这是有意义的,因为这是一个任意的理由,要求GetMoveTargets支持使用NIL板引用的调用。

The particular placement of an individual piece at any given time is a property of an individual game of chess, and equally the VALID moves that may be POSSIBLE for any given piece are dependent upon the placement of OTHER pieces in the game.

一个棋子在任何给定时间的特定位置是棋子的一个属性,同样,任何棋子可能的有效移动也取决于游戏中其他棋子的位置。

TChessPiece.GetMoveTargets does not need knowledge of the current game state. This is the responsibility of a TChessGame. And a TChessPiece does not need a reference to a game or to a board to determine the valid move targets from a given current position. The board constraints (8 ranks and files) are domain constants, not properties of a given board instance.

TChessPiece。GetMoveTargets不需要了解当前游戏状态。这是TChessGame的责任。而且,TChessPiece不需要参考游戏或棋盘来确定当前位置的有效移动目标。板约束(8个等级和文件)是域常量,而不是给定板实例的属性。

So, a TChessGame is required that encapsulates the knowledge that combines awareness of a board, the pieces and - crucially - the rules, but the board and the pieces do not need knowledge of each other OR of the game.

因此,需要一个TChessGame来封装对棋盘、棋子以及——关键是——规则的认识,但棋盘和棋子不需要相互了解或了解游戏。

It may seem tempting to put the rules pertaining to different pieces in the class for the piece type itself, but this is a mistake imho, since many of the rules are based on interactions with other pieces and in some cases with specific piece types. Such "big picture" behaviours require a degree of over-sight (read: overview) of the total game state that is not appropriate in a specific piece class.

在类中为片段类型本身设置与不同片段相关的规则似乎很有吸引力,但这在imho中是一个错误,因为许多规则都是基于与其他片段的交互,在某些情况下还基于特定的片段类型。这样的“大图景”行为需要对整个游戏状态进行一定程度的过度关注(参见:概述),这在特定的片类中是不合适的。

e.g. a TChessPawn may determine that a valid move target is one or two squares forward or one square diagonally forward if either of those diagonal squares are occupied. However, if the movement of the pawn exposes the King to a CHECK situation, then the pawn is not movable at all.

例如,如果一个TChessPawn占用了这些对角线上的方块,那么它就可以确定一个有效的移动目标是向前移动的一个或两个方块,还是向斜前方移动一个正方形。但是,如果典当的移动将国王暴露在检查的情况下,那么典当根本就不能移动。

I would approach this by simply allowing the pawn class to indicate all POSSIBLE move targets - 1 or 2 squares forward and both diagonally forward squares. The TChessGame then determines which of these is valid by reference to occupancy of those move targets and game state. 2 squares forward is only possible if the pawn is on it's home rank, forward squares being occupied BLOCK a move = invalid target, UNoccupied diagonal squares FACILITATE a move, and if any otherwise valid move exposes the King, then that move is also invalid.

我可以通过简单地让典当类指示所有可能的移动目标来实现这一点——1或2个向前的方块和两个对角线向前的方块。然后,TChessGame通过引用移动目标的占用率和游戏状态来确定哪些是有效的。只有当兵在本垒上时,才有可能向前移动2个方块,向前移动的方块被占据a块移动=无效目标,未被占用的对角线方块会促进移动,如果其他有效的移动暴露了国王,那么移动也无效。

Again, the temptation might be to put the generally applicable rules in the base TChessPiece class (e.g. does a given move expose the King?), but applying that rule requires awareness of the overall game state - i.e. placement of other pieces - so it more properly belongs as a generalised behaviour of the TChessGame class, imho

的诱惑可能将普遍适用的规则TChessPiece基类(例如给定的举动让国王吗?),但应用该规则需要整体游戏状态的意识——即位置的其他部分——因此更恰当的属于TChessGame类的普遍行为,恕我直言

In addition to move targets, pieces also need to indicate CaptureTargets, which in the case of most pieces is the same, but in some cases quite different - pawn being a good example. But again, which - if any - of all potential captures is effective for any given move is - imho - an assessment of the game rules, not the behaviour of a piece or class of pieces.

除了移动目标之外,棋子还需要指示CaptureTargets,在大多数情况下,这是相同的,但是在某些情况下是完全不同的——典当就是一个很好的例子。但同样,如果有的话,所有可能的捕捉对任何给定的动作都是有效的——imho——是对游戏规则的评估,而不是对一段或一组棋子的行为的评估。

As is the case in 99% of such situations (ime - ymmv) the dilemma is perhaps better solved by changing the class design to better represent the problem being modelled, not finding a way to shoehorn the class design into an arbitrary file organisation.

就像99%的这种情况(ime - ymmv)一样,通过改变类的设计来更好地代表正在建模的问题,而不是找到将类设计塞进任意文件组织的方法,这可能会更好地解决这个难题。

#3


16  

One solution could be the introduction of a third unit which contains interface declarations (IBoard and IPiece).

一种解决方案是引入第三个单元,其中包含接口声明(IBoard和IPiece)。

Then the interface sections of the two units with class declarations can refer to the other class by its interface:

然后两个具有类声明的单元的接口部分可以通过其接口指向另一个类:

TChessBoard = class(TInterfacedObject, IBoard)
private
  FBoard : array [1..8, 1..8] of IPiece;
...
end;

and

TChessPiece = class abstract(TInterfacedObject, IPiece)
public
   procedure GetMoveTargets (BoardPos: TPoint; const Board: IBoard; 
     MoveTargetList: TList <TPoint>);
...
end;

(The const modifier in GetMoveTargets avoids unnecessary reference counting)

(GetMoveTargets中的const修饰符避免不必要的引用计数)

#4


2  

It would be better to move ChessPiece class into ChessBoard unit.
If for some reason you can't, try to put one uses clause to the implementation part in one unit and leave the other one in the interface part.

最好把切斯皮埃斯班搬到棋盘上。如果由于某些原因您不能,试着将一个use子句放在一个单元中的实现部分,而将另一个在接口部分中保留。

#5


1  

With Delphi Prism you can spread your namespaces over separate files, so there you would be able to solve it in a clean way.

使用Delphi Prism,您可以将名称空间分散到不同的文件中,这样您就能够以一种干净的方式解决它。

The way units work are just fundamentally broken with their current Delphi implementation. Just look at how "db.pas" needed to have TField, TDataset, TParam, etc in one monstrous .pas file because their interfaces reference each other.

单元工作的方式与当前的Delphi实现基本没有关系。看看db。pas"需要在一个巨大的。pa文件中包含TField、TDataset、TParam等,因为它们的接口相互引用。

Anyway, you can always move code to a separate file and include them with {$include ChessBoard_impl.inc} for example. That way you can split stuff over files and have separate versions via your vcs. However, it's just a bit unhandy to edit files that way.

无论如何,您总是可以将代码移动到一个单独的文件中,并将其包含为{$include ChessBoard_impl。例如公司}。这样,你就可以在文件上分割文件,并通过风投建立不同的版本。但是,这样编辑文件有点不方便。

The best long-term solution would be to urge embarcadero to ditch some of the ideas that made sense in 1970 when pascal was born, but that are not much more than a pain in the ass for developers nowadays. A one-pass compiler is one of those.

最好的长期解决方案是敦促embarcadero放弃一些在1970年pascal诞生时就有意义的想法,但这对现在的开发人员来说不过是麻烦。一遍编译器就是其中之一。

#6


0  

It does not look like TChessBoard.FBoard needs to be an array of TChessPiece, it can as well be of TObject and be downcasted in ChessPiece.pas.

它看起来不像TChessBoard。FBoard需要一个TChessPiece数组,它也可以是TObject,并在chesspiec .pas中被降级。

#7


0  

Another approach:

另一种方法:

Make your board of tBaseChessPiece. It's abstract but contains the definitions you need to refer to.

做你的板子。它是抽象的,但包含您需要引用的定义。

The internal workings are in tChessPiece which descends from tBaseChessPiece.

内部工作在tChessPiece中,它来自tBaseChessPiece。

I do agree that Delphi's handling of things that refer to each other is bad--about the worst feature of the language. I've long called for forward declarations that work across units. The compiler would have the information it needs, it wouldn't break the one-pass nature that makes it so fast.

我同意德尔菲对相互联系的事物的处理是不好的——这是语言最糟糕的特点。我一直呼吁在单位之间工作的前向声明。编译器会得到它所需要的信息,它不会破坏使它如此快速的单步性。

#8


0  

what about this approach:

这个方法:

chess board unit:

棋盘单元:

TBaseChessPiece = class 

public

   procedure GetMoveTargets (BoardPos : TPoint; Board : TChessBoard; MoveTargetList : TList <TPoint>); virtual; abstract;

...

TChessBoard = class
private
  FBoard : array [1..8, 1..8] of TChessPiece;

  procedure InitializePiecesWithDesiredClass;
...

pieces unit:

件单位:

TYourPiece = class TBaseChessPiece

public 

   procedure GetMoveTargets (BoardPos : TPoint; Board : TChessBoard; MoveTargetList : TList <TPoint>);override;

...

In this aproach chess board unit will include the reference of pieces unit only in implementation section(due to the method that will in fact create the objects) and pieces unit will have a reference to chess board unit in interface. If I'm not wrong this handle your problem in a easy way...

在这个象棋棋盘单元中,棋子单元将只包含在执行部分的引用(由于实际上创建棋子的方法),棋子单元将在接口中引用棋子单元。如果我没弄错的话,这可以很容易地解决你的问题……

#9


0  

Derive TChessBoard from TObject

TChessBoard来自TObject

TChessBoard = class(TObject)

TChessBoard =类(TObject)

then you can declare procedure GetMoveTargets (BoardPos : TPoint; Board : TObject; MoveTargetList : TList );

然后可以声明GetMoveTargets (BoardPos: TPoint;董事会:TObject;MoveTargetList:TList);

when you call the proc, use SELF as the Board object (if you are calling it from there), then you can reference it with

调用proc时,使用SELF作为Board对象(如果是从那里调用),然后可以引用它

(Board as TChessBoard). and access the properties etc from that.

(董事会TChessBoard)。然后访问属性等等。