为什么这段代码有效?在这种情况下,Delphi 如何实例化一个类?

ral*_*ash 2 delphi class

我正在练习 Delphi 跟踪,并遵循 Delphi 如何为表单生成代码,回答了如下基本问题之一:

unit uLeap;

interface

type
  TSYear = class
    public
      { public declarations here }
      function isLeap(y: integer): boolean;
  end;

var
TYear: TSYear;

implementation

function TSYear.isLeap(y: integer): boolean;
begin
  result := ((y mod 4) = 0) and (((y mod 400) = 0) or ((y mod 100) <> 0));
end;

end.
Run Code Online (Sandbox Code Playgroud)

代码编译时没有任何抱怨,我可以逐步运行它,并且“isLeap”函数以这种方式从另一个单元多次调用:

procedure YearTest.year_divisible_by_4_not_divisible_by_100_leap_year;
begin
  assert.IsTrue(TYear.IsLeap(1996), 'Expected ''true'', 1996 is a leap year.');
end;
...
Run Code Online (Sandbox Code Playgroud)

我从来没有明确地创建过这个类的实例,但似乎 Delphi 正在某处做它,也许是在声明 TYear 时?这是一种有效的方式吗?

尽管通过了所有测试,代码还是被拒绝了,因为它不是以传统方式完成的。我肯定会最终以不同的方式接受它,但是,除了糟糕的命名之外,为什么这有效?这段代码会在这个简单的例子中我看不到的地方引起问题吗?

And*_*and 10

我从来没有明确地创建过这个类的实例,但似乎 Delphi 正在某处做它,也许是在声明 TYear 时?

不,Delphi 不会自动创建您的实例。当您声明一个类类型的变量时,它只是一个可以指向有效实例的指针变量。但是您必须始终自己创建此实例,并将指针保存在变量中:

SYear := TSYear.Create; // create a `TSYear` object and save its address in `SYear`
Run Code Online (Sandbox Code Playgroud)

这是一种有效的方式吗?

不。

[W] 为什么这有效?

因为您很幸运:该isLeap函数不会访问类实例上的任何字段。

这段代码会在这个简单的例子中我看不到的地方引起问题吗?

如果该函数一直在使用类实例中的任何字段,那么如果幸运的话,您最终会得到一个 AV,而如果不幸的话,您会得到内存损坏。

解决方案是创建一个实例并使用它:

SYear := TSYear.Create;
try
  ShowMessage(BoolToStr(SYear.IsLeap(2000), True));
finally
  SYear.Free;
end;
Run Code Online (Sandbox Code Playgroud)

或者,由于您显然不需要任何实例变量来确定年份是否为闰年,因此最好将此class方法设为:

SYear := TSYear.Create; // create a `TSYear` object and save its address in `SYear`
Run Code Online (Sandbox Code Playgroud)

这样,它可以在没有任何类实例的情况下被调用:TSYear.IsLeap(2000)。请注意,这TSYear是类(类型)名称,而不是该类型的变量。

有关所有这些概念的概念介绍,请参阅文档