真的需要 Delphi 记录构造函数吗?
Are Delphi record constructors really needed?
情况
我正在研究 Nick Hodges 的 "More Coding in Delphi",他正在使用 TFraction
记录来解释运算符重载。这条记录是我自己写的:
type
TFraction = record
strict private
aNumerator: integer;
aDenominator: integer;
function GCD(a, b: integer): integer;
public
constructor Create(aNumerator: integer; aDenominator: integer);
procedure Reduce;
class operator Add(fraction1, fraction2: TFraction): TFraction;
class operator Subtract(fraction1, fraction2: TFraction): TFraction;
//... implicit, explicit, multiply...
property Numerator: integer read aNumerator;
property Denominator: integer read aDenominator;
end;
当然,我必须创建一个构造函数,因为在 Q(有理数)中我必须有一个不等于零的分母。
constructor TFraction.Create(aNumerator, aDenominator: integer);
begin
if (aDenominator = 0) then
begin
raise Exception.Create('Denominator cannot be zero in rationals!');
end;
if ( (aNumerator < 0) or (aDenominator < 0) ) then
begin
Self.aNumerator := -aNumerator;
Self.aDenominator := -aDenominator;
end
else
begin
Self.aNumerator := aNumerator;
Self.aDenominator := aDenominator;
end;
end;
问题
由于运算符重载了return一个TFraction
,我打算这样定义一个操作:
class operator TFraction.Add(fraction1, fraction2: TFraction): TFraction;
var
tmp: TFraction;
begin
//simple algorithm of the sum
tmp := TFraction.Create(fraction1.Numerator*fraction2.Denominator+fraction1.Denominator*fraction2.Numerator, fraction1.Denominator*fraction2.Denominator);
tmp.Reduce;
//return the result
Result := tmp;
end;
正如您在这里看到的,我正在创建一个 tmp
,它是从函数 return 编辑的。
当我阅读 Marco Cantu 的书时,他使用了另一种方法:
class operator TFraction.Add(fraction1, fraction2: TFraction): TFraction;
begin
Result.aNumerator := (fraction1.Numerator*fraction2.Denominator+fraction1.Denominator*fraction2.Numerator);
Result.aDenominator := fraction1.Denominator*fraction2.Denominator;
end;
我做了一些测试,我看到两者都给了我正确的结果,但有些东西我不明白。在第一种方法中,我声明 tmp,然后调用构造函数,这样我就可以 return 一个 TFraction
。在第二种方法中,我没有创建任何东西,因为记录有一个自动构造函数。事实上,文档说:
Records are constructed automatically, using a default no-argument
constructor, but classes must be explicitly constructed. Because
records have a default no-argument constructor, any user-defined
record constructor must have one or more parameters.
这里我有一个用户定义的记录构造函数。所以:
是否不需要第一种方法的 tmp
上的构造函数调用?如果我想调用 Reduce
(这是一个过程),我需要创建一个变量。 Result
只是 return 复制了 tmp
而没有创建任何东西吗?
第二种方式,Result.aNumerator
和Result.aDenominator
是自动创建构造函数的参数吗?
记录构造函数并不神奇。它只是一个实例方法,就像任何其他方法一样。你写:
tmp := TFraction.Create(...);
但你也可以这样写:
tmp.Create(...);
我个人觉得两者都不是特别有用,因为我习惯了 classes 的构造函数调用语义,它分配和默认初始化内存,然后调用构造函数方法。
尤其是第二个变体让我很生气,因为这看起来像新手 Delphi 程序员在开始并尝试创建 class 的实例时犯的 classic 错误.如果 TFraction
是 class,那段代码就不好用了,但作为记录,它没问题。
如果是我,我会摆脱记录构造函数,而是使用静态 class 函数返回记录类型的新实例。我的惯例是命名这样的东西New
。不过这些都是个人喜好问题。
如果你这样做,它会像这样声明:
class function New(aNumerator, aDenominator: Integer): TFraction; static;
它会这样实现:
class function TFraction.New(aNumerator, aDenominator: Integer): TFraction;
begin
Result.aNumerator := ...;
Result.aDenominator := ...;
end;
你可以这样称呼它:
frac := TFraction.New(num, denom);
但正如我所说,这是一个偏好问题。如果您喜欢记录构造函数,请随时坚持使用它们。
你问能不能跳过构造函数。在记录分配方面,是的,您可以跳过它。就 运行 构造函数中的代码而言,只有您可以确定。您是否要执行该代码?
如果您希望执行该代码,但又不想使用临时变量,那么您可以这样编写代码:
class operator TFraction.Add(fraction1, fraction2: TFraction): TFraction;
begin
Result.Create(
fraction1.Numerator*fraction2.Denominator + fraction1.Denominator*fraction2.Numerator,
fraction1.Denominator*fraction2.Denominator
);
Result.Reduce;
end;
或者,如果您更喜欢静态 class 函数,它将是:
class operator TFraction.Add(fraction1, fraction2: TFraction): TFraction;
begin
Result := TFraction.New(
fraction1.Numerator*fraction2.Denominator + fraction1.Denominator*fraction2.Numerator,
fraction1.Denominator*fraction2.Denominator
);
Result.Reduce;
end;
情况
我正在研究 Nick Hodges 的 "More Coding in Delphi",他正在使用 TFraction
记录来解释运算符重载。这条记录是我自己写的:
type
TFraction = record
strict private
aNumerator: integer;
aDenominator: integer;
function GCD(a, b: integer): integer;
public
constructor Create(aNumerator: integer; aDenominator: integer);
procedure Reduce;
class operator Add(fraction1, fraction2: TFraction): TFraction;
class operator Subtract(fraction1, fraction2: TFraction): TFraction;
//... implicit, explicit, multiply...
property Numerator: integer read aNumerator;
property Denominator: integer read aDenominator;
end;
当然,我必须创建一个构造函数,因为在 Q(有理数)中我必须有一个不等于零的分母。
constructor TFraction.Create(aNumerator, aDenominator: integer);
begin
if (aDenominator = 0) then
begin
raise Exception.Create('Denominator cannot be zero in rationals!');
end;
if ( (aNumerator < 0) or (aDenominator < 0) ) then
begin
Self.aNumerator := -aNumerator;
Self.aDenominator := -aDenominator;
end
else
begin
Self.aNumerator := aNumerator;
Self.aDenominator := aDenominator;
end;
end;
问题
由于运算符重载了return一个TFraction
,我打算这样定义一个操作:
class operator TFraction.Add(fraction1, fraction2: TFraction): TFraction;
var
tmp: TFraction;
begin
//simple algorithm of the sum
tmp := TFraction.Create(fraction1.Numerator*fraction2.Denominator+fraction1.Denominator*fraction2.Numerator, fraction1.Denominator*fraction2.Denominator);
tmp.Reduce;
//return the result
Result := tmp;
end;
正如您在这里看到的,我正在创建一个 tmp
,它是从函数 return 编辑的。
当我阅读 Marco Cantu 的书时,他使用了另一种方法:
class operator TFraction.Add(fraction1, fraction2: TFraction): TFraction;
begin
Result.aNumerator := (fraction1.Numerator*fraction2.Denominator+fraction1.Denominator*fraction2.Numerator);
Result.aDenominator := fraction1.Denominator*fraction2.Denominator;
end;
我做了一些测试,我看到两者都给了我正确的结果,但有些东西我不明白。在第一种方法中,我声明 tmp,然后调用构造函数,这样我就可以 return 一个 TFraction
。在第二种方法中,我没有创建任何东西,因为记录有一个自动构造函数。事实上,文档说:
Records are constructed automatically, using a default no-argument constructor, but classes must be explicitly constructed. Because records have a default no-argument constructor, any user-defined record constructor must have one or more parameters.
这里我有一个用户定义的记录构造函数。所以:
是否不需要第一种方法的
tmp
上的构造函数调用?如果我想调用Reduce
(这是一个过程),我需要创建一个变量。Result
只是 return 复制了tmp
而没有创建任何东西吗?第二种方式,
Result.aNumerator
和Result.aDenominator
是自动创建构造函数的参数吗?
记录构造函数并不神奇。它只是一个实例方法,就像任何其他方法一样。你写:
tmp := TFraction.Create(...);
但你也可以这样写:
tmp.Create(...);
我个人觉得两者都不是特别有用,因为我习惯了 classes 的构造函数调用语义,它分配和默认初始化内存,然后调用构造函数方法。
尤其是第二个变体让我很生气,因为这看起来像新手 Delphi 程序员在开始并尝试创建 class 的实例时犯的 classic 错误.如果 TFraction
是 class,那段代码就不好用了,但作为记录,它没问题。
如果是我,我会摆脱记录构造函数,而是使用静态 class 函数返回记录类型的新实例。我的惯例是命名这样的东西New
。不过这些都是个人喜好问题。
如果你这样做,它会像这样声明:
class function New(aNumerator, aDenominator: Integer): TFraction; static;
它会这样实现:
class function TFraction.New(aNumerator, aDenominator: Integer): TFraction;
begin
Result.aNumerator := ...;
Result.aDenominator := ...;
end;
你可以这样称呼它:
frac := TFraction.New(num, denom);
但正如我所说,这是一个偏好问题。如果您喜欢记录构造函数,请随时坚持使用它们。
你问能不能跳过构造函数。在记录分配方面,是的,您可以跳过它。就 运行 构造函数中的代码而言,只有您可以确定。您是否要执行该代码?
如果您希望执行该代码,但又不想使用临时变量,那么您可以这样编写代码:
class operator TFraction.Add(fraction1, fraction2: TFraction): TFraction;
begin
Result.Create(
fraction1.Numerator*fraction2.Denominator + fraction1.Denominator*fraction2.Numerator,
fraction1.Denominator*fraction2.Denominator
);
Result.Reduce;
end;
或者,如果您更喜欢静态 class 函数,它将是:
class operator TFraction.Add(fraction1, fraction2: TFraction): TFraction;
begin
Result := TFraction.New(
fraction1.Numerator*fraction2.Denominator + fraction1.Denominator*fraction2.Numerator,
fraction1.Denominator*fraction2.Denominator
);
Result.Reduce;
end;