7.5.3 完整的TDate类
在本章中,我向您展示了TDate类不同版本的源代码的片段。第一个版本基于三个整数来存储年份、月份和日期;第二个版本使用RTL提供的TDateTime类型的字段。以下是定义TDate类的单元的完整接口部分:
unit Dates;
interface
typeTDate = classprivateFDate: TDateTime;publicconstructor Create; overload;constructor Create(Month, Day, Year: Integer); overload;procedure SetValue(Month, Day, Year: Integer); overload;procedure SetValue(NewDate: TDateTime); overload;function LeapYear: Boolean;procedure Increase(NumberOfDays: Integer = 1);procedure Decrease(NumberOfDays: Integer = 1);function GetText: string;end;
新方法Increase和Decrease(它们都为其参数设置了默认值)的目的非常容易理解。如果没有参数调用它们,它们将更改日期的值为下一天或前一天。如果NumberOfDays参数是调用的一部分,调用将加上或减去该数字:
procedure TDate.Increase(NumberOfDays: Integer = 1);
beginFDate := FDate + NumberOfDays;
end;
GetText方法使用DateToStr函数进行转换,返回带有格式化日期的字符串:
function TDate.GetText: string;
beginGetText := DateToStr(FDate);
end;
我们已经在前面的章节中看到了大部分的方法,所以我就不提供完整的列表了;你可以在我为测试该类而编写的 ViewDate
示例的代码中找到。这个窗体比书中的其他窗体要复杂一些,它有一个显示日期的标题和六个按钮,可以用来修改对象的值。 你可以在图 7.3 中看到 ViewDate
示例运行时的主窗体。为了让标签组件看起来更美观,我给了它一个大字体,让它和窗体一样宽,将其对齐属性设置为 taCenter
,并将其 AutoSize
属性设置为 False
。
图 7.3:ViewDate
应用程序启动时的输出结果
该程序的启动代码位于窗体的 OnCreate
事件处理程序中。在相应的方法中,我们创建一个 TDate
类的实例,初始化该对象,然后在标签的文本中显示其文字说明,如图 7.3 所示。
procedure TDateForm.FormCreate(Sender: TObject);
beginADay := TDate.Create;LabelDate.Text := ADay.GetText;
end;
ADay是该窗体类TDateForm的一个私有字段。顺便说一下,当您更改窗体的Name属性为DateForm时,开发环境会自动选择类的名称。具体的Date对象在窗体创建时创建(建立了我们之前看到的在person类和Date子对象之间相同的关系),然后随着窗体一起被销毁。
procedure TDateForm.FormDestroy(Sender: TObject);
beginADay.Free;
end;
当用户点击六个按钮中的一个时,我们需要对 ADay 对象应用相应的方法,然后在标签中显示日期的新值:
procedure TDateForm.BtnTodayClick(Sender: TObject);
beginADay.SetValue(Today);LabelDate.Text := ADay.GetText;
end;
编写上述方法的另一种方式是销毁当前对象并创建一个新对象:
procedure TDateForm.BtnTodayClick(Sender: TObject);
beginADay.Free;ADay := TDate.Create;LabelDate.Text := ADay.GetText;
end;
在这种特殊情况下,创建一个新对象并销毁现有对象并不是最好的方法,因为当我们只需要更改对象的值时,这样做会产生大量的开销。不过,它允许我演示一些 Object Pascal 技术。 首先要注意的是,在赋值新对象之前,我们要先销毁前一个对象。事实上,赋值操作会替换引用,将对象留在内存中(即使没有指针引用它)。当你将一个对象赋值给另一个对象时,编译器会简单地将内存中的对象引用复制到新的对象引用中。
一个附带问题是如何将数据从一个对象复制到另一个对象。这种情况非常简单,因为只有一个字段和一个初始化方法。一般来说,如果要更改现有对象内部的数据,就必须复制每个字段,或者提供一个特定的方法来复制所有内部数据。有些类有一个 Assign 方法,可以执行深度复制操作。
注解:更准确地说,在运行时库中,所有继承自TPersisent的类都有Assign方法。然而,大多数继承自TComponent的类都没有实现它,所以在调用它时会引发异常。原因在于运行时库支持的流机制和对TPersisent类型属性的支持。不过,这一点过于复杂,本书目前还无法深入探讨。