Lazarus / Delphi - Create vs TSomeClass.Create inside constructor - 为什么这会造成麻烦?
Lazarus / Delphi - Create vs TSomeClass.Create inside constructor - why is this causing trouble?
突出显示
这个构造函数:
constructor TCoords.Create(const AX, AY: Integer);
begin
TCoords.Create(Point(AX, AY));
end;
已确认 Linux Lazarus 2 和 Windows Delphi XE6 均出现故障。
这可能是一个错误吗?
我是 Lazarus 中的 OOP 新手/Delphi,请原谅新手可能犯的错误。谢谢。
我似乎无法理解为什么以下序列的 Lazarus / (Delphi-like) 自定义,非常基本,一个对象将不起作用。我已经尝试调试了几个小时,从那时起我发现:
什么有效:
调用一个不带参数的构造函数,并直接调用带 TPoint 参数的构造函数。
什么没有:
调用这个:
constructor Create(const AX, AY: Integer);
但是,我发现这会起作用 - 但是 仅当 在构造函数中没有其 class 名称的情况下调用。为什么会引起麻烦?
声明
// WORKS - this creates instance of TCoords initialized to PointOutOfReach
constructor Create; reintroduce;
// WORKS - this creates instance of TCoords initialized to user coordinates
constructor Create(const ACoords: TPoint);
// DOES NOT WORK, strangely returns Point(0, 0), if called with class name
// WORKS only if called without class name - confusing or error on my side?
constructor Create(const AX, AY: Integer);
来电
// OK - WORKING
NewCoords := TCoords.Create;
NewCoords.X:=12;
NewCoords.Y:=120;
ShowMessage(NewCoords.X.ToString + ' : ' + NewCoords.Y.ToString);
NewCoords.Free;
// OK - WORKING
NewCoords := TCoords.Create(Point(12, 120));
ShowMessage(NewCoords.X.ToString + ' : ' + NewCoords.Y.ToString);
NewCoords.Free;
// NOT WORKING as expected
NewCoords := TCoords.Create(12, 120);
ShowMessage(NewCoords.X.ToString + ' : ' + NewCoords.Y.ToString);
NewCoords.Free;
具有 TCoords 对象定义的坐标单位
unit Coords;
{$mode objfpc}{$H+}
interface
uses
Classes;
type
// Flexible X,Y coordinates object.
TCoords = class(TObject)
// these declarations are accessible within this unit only
private
// this is the variable we are working with
FCoords: TPoint;
// property for this function is unnecessary, but I like it as it is
function IsInitialized: Boolean;
// these declarations are accessible to all
public
// this creates instance of TCoords initialized to PointOutOfReach
constructor Create; reintroduce;
// this creates instance of TCoords initialized to user coordinates
constructor Create(const ACoords: TPoint);
// THIS ONE DOES NOT WORK, strangely returns Point(0, 0)
constructor Create(const AX, AY: Integer);
// this indicates if instance was initialized or not by the user
property Initialized: Boolean read IsInitialized;
// this works directly with private FCoords variable storing coordinates
property P: TPoint read FCoords write FCoords;
// these two are shortcuts for X,Y coordinates' direct access
property X: Integer read FCoords.X write FCoords.X;
property Y: Integer read FCoords.Y write FCoords.Y;
end;
implementation
var
// this gets initialized when loading this unit
PointOutOfReach: TPoint;
constructor TCoords.Create;
begin
// this is the same as `inherited`, but I like to be explicit
inherited Create;
// since called without argument, we have to ensure, there is some nonsense
FCoords := PointOutOfReach;
end;
constructor TCoords.Create(const ACoords: TPoint);
begin
// this is the same as `Create`, but I like to be explicit
TCoords.Create;
// in the previous mandatory call we have initialized FCoords already
// but to PointOutOfReach; here we overwrite it with user coordinates
FCoords := ACoords;
end;
constructor TCoords.Create(const AX, AY: Integer);
begin
// this is the same as `Create(TPoint)`, but I like to be explicit
// TCoords.Create(Point(AX, AY));
// Why can't I call the above, shouldn't it be the very same?
Create(Point(AX, AY));
end;
function TCoords.IsInitialized: Boolean;
begin
// this returns True in case FCoords has been initialized
// initialized means here for the FCoords point to be different from PointOutOfReach
// achieved either by calling `Create(APoint)`, or later overwriting PointOutOfReach
Result := FCoords <> PointOutOfReach;
end;
initialization
// initialize PointOutOfReach to "impossible" coordinates when loading unit
PointOutOfReach := Point(MAXINT, MAXINT);
end.
提前谢谢你,我自己似乎看不出这两者之间的区别。
更正了主要错误 - 没有变化
构造函数声明中缺失的 overload;
已更正。遗憾的是,仍然从最后一个构造函数中获得 0,0 坐标。
这是您声明属性的方式 X
并且 Y
将它们更改为此
property X: Integer read FCoords.X write SetCoordX;
property Y: Integer read FCoords.Y write SetCoordY;
procedure TCoords.SetCoordX(const Value: Integer);
begin
FCoords.X := Value;
end;
procedure TCoords.SetCoordY(const Value: Integer);
begin
FCoords.Y := Value;
end;
这与 Delphi 如何分配属性有关。在您的情况下,您正在分配给编译器在检索 X
.
的值时添加的隐式变量
我似乎不记得我是在哪里读到的,我会找到它并编辑我的答案
David Heffernan在评论中说明了我的方法不起作用的原因,让我引用:
TCoords.Create(Point(AX, AY))
creates a new instance. When you do this inside a constructor you now have two instances. Replace it with Create(Point(AX, AY))
.
谢谢您的解释!
即使解决了,我认为更好的方法是 不要链接那些 constructor
。
应用此规则有效,使用未链接的构造函数的工作片段:
constructor TCoords.Create;
begin
inherited Create;
// since called without argument, we have to ensure,
// there are some corner-case coordinates, so that we can
// differentiate between a [0:0] and uninitialized state
FCoords := PointOutOfReach;
end;
constructor TCoords.Create(const ACoords: TPoint);
begin
inherited Create;
FCoords := ACoords;
end;
constructor TCoords.Create(const AX, AY: Integer);
begin
inherited Create;
FCoords := Point(AX, AY);
end;
PS:为了让代码正常工作,到目前为止我认为没有必要像 Nasreddine Galfout's .
那样应用 "setters"
突出显示
这个构造函数:
constructor TCoords.Create(const AX, AY: Integer);
begin
TCoords.Create(Point(AX, AY));
end;
已确认 Linux Lazarus 2 和 Windows Delphi XE6 均出现故障。
这可能是一个错误吗?
我是 Lazarus 中的 OOP 新手/Delphi,请原谅新手可能犯的错误。谢谢。
我似乎无法理解为什么以下序列的 Lazarus / (Delphi-like) 自定义,非常基本,一个对象将不起作用。我已经尝试调试了几个小时,从那时起我发现:
什么有效:
调用一个不带参数的构造函数,并直接调用带 TPoint 参数的构造函数。
什么没有:
调用这个:
constructor Create(const AX, AY: Integer);
但是,我发现这会起作用 - 但是 仅当 在构造函数中没有其 class 名称的情况下调用。为什么会引起麻烦?
声明
// WORKS - this creates instance of TCoords initialized to PointOutOfReach
constructor Create; reintroduce;
// WORKS - this creates instance of TCoords initialized to user coordinates
constructor Create(const ACoords: TPoint);
// DOES NOT WORK, strangely returns Point(0, 0), if called with class name
// WORKS only if called without class name - confusing or error on my side?
constructor Create(const AX, AY: Integer);
来电
// OK - WORKING
NewCoords := TCoords.Create;
NewCoords.X:=12;
NewCoords.Y:=120;
ShowMessage(NewCoords.X.ToString + ' : ' + NewCoords.Y.ToString);
NewCoords.Free;
// OK - WORKING
NewCoords := TCoords.Create(Point(12, 120));
ShowMessage(NewCoords.X.ToString + ' : ' + NewCoords.Y.ToString);
NewCoords.Free;
// NOT WORKING as expected
NewCoords := TCoords.Create(12, 120);
ShowMessage(NewCoords.X.ToString + ' : ' + NewCoords.Y.ToString);
NewCoords.Free;
具有 TCoords 对象定义的坐标单位
unit Coords;
{$mode objfpc}{$H+}
interface
uses
Classes;
type
// Flexible X,Y coordinates object.
TCoords = class(TObject)
// these declarations are accessible within this unit only
private
// this is the variable we are working with
FCoords: TPoint;
// property for this function is unnecessary, but I like it as it is
function IsInitialized: Boolean;
// these declarations are accessible to all
public
// this creates instance of TCoords initialized to PointOutOfReach
constructor Create; reintroduce;
// this creates instance of TCoords initialized to user coordinates
constructor Create(const ACoords: TPoint);
// THIS ONE DOES NOT WORK, strangely returns Point(0, 0)
constructor Create(const AX, AY: Integer);
// this indicates if instance was initialized or not by the user
property Initialized: Boolean read IsInitialized;
// this works directly with private FCoords variable storing coordinates
property P: TPoint read FCoords write FCoords;
// these two are shortcuts for X,Y coordinates' direct access
property X: Integer read FCoords.X write FCoords.X;
property Y: Integer read FCoords.Y write FCoords.Y;
end;
implementation
var
// this gets initialized when loading this unit
PointOutOfReach: TPoint;
constructor TCoords.Create;
begin
// this is the same as `inherited`, but I like to be explicit
inherited Create;
// since called without argument, we have to ensure, there is some nonsense
FCoords := PointOutOfReach;
end;
constructor TCoords.Create(const ACoords: TPoint);
begin
// this is the same as `Create`, but I like to be explicit
TCoords.Create;
// in the previous mandatory call we have initialized FCoords already
// but to PointOutOfReach; here we overwrite it with user coordinates
FCoords := ACoords;
end;
constructor TCoords.Create(const AX, AY: Integer);
begin
// this is the same as `Create(TPoint)`, but I like to be explicit
// TCoords.Create(Point(AX, AY));
// Why can't I call the above, shouldn't it be the very same?
Create(Point(AX, AY));
end;
function TCoords.IsInitialized: Boolean;
begin
// this returns True in case FCoords has been initialized
// initialized means here for the FCoords point to be different from PointOutOfReach
// achieved either by calling `Create(APoint)`, or later overwriting PointOutOfReach
Result := FCoords <> PointOutOfReach;
end;
initialization
// initialize PointOutOfReach to "impossible" coordinates when loading unit
PointOutOfReach := Point(MAXINT, MAXINT);
end.
提前谢谢你,我自己似乎看不出这两者之间的区别。
更正了主要错误 - 没有变化
构造函数声明中缺失的 overload;
已更正。遗憾的是,仍然从最后一个构造函数中获得 0,0 坐标。
这是您声明属性的方式 X
并且 Y
将它们更改为此
property X: Integer read FCoords.X write SetCoordX;
property Y: Integer read FCoords.Y write SetCoordY;
procedure TCoords.SetCoordX(const Value: Integer);
begin
FCoords.X := Value;
end;
procedure TCoords.SetCoordY(const Value: Integer);
begin
FCoords.Y := Value;
end;
这与 Delphi 如何分配属性有关。在您的情况下,您正在分配给编译器在检索 X
.
我似乎不记得我是在哪里读到的,我会找到它并编辑我的答案
David Heffernan在评论中说明了我的方法不起作用的原因,让我引用:
TCoords.Create(Point(AX, AY))
creates a new instance. When you do this inside a constructor you now have two instances. Replace it withCreate(Point(AX, AY))
.
谢谢您的解释!
即使解决了,我认为更好的方法是 不要链接那些 constructor
。
应用此规则有效,使用未链接的构造函数的工作片段:
constructor TCoords.Create;
begin
inherited Create;
// since called without argument, we have to ensure,
// there are some corner-case coordinates, so that we can
// differentiate between a [0:0] and uninitialized state
FCoords := PointOutOfReach;
end;
constructor TCoords.Create(const ACoords: TPoint);
begin
inherited Create;
FCoords := ACoords;
end;
constructor TCoords.Create(const AX, AY: Integer);
begin
inherited Create;
FCoords := Point(AX, AY);
end;
PS:为了让代码正常工作,到目前为止我认为没有必要像 Nasreddine Galfout's