(delphi11最新学习资料) Object Pascal 学习笔记---第2章第三节(简单的自定义数据类型)

本文详细介绍了ObjectPascal中的用户自定义数据类型,包括子界类型、枚举类型、集合类型、类型别名、子范围、枚举的范围和集合运算符,以及如何在Delphi11中定义和使用这些类型。
摘要由CSDN通过智能技术生成

Object Pascal 手册,Delphi 11 编程语言的完整介绍 作者: Marco Cantu 笔记:豆豆爸

2.3 简单的用户自定义数据类型

​ 除了类型的概念,Niklaus Wirth 在最初的 Pascal 语言中引入的一个伟大想法是在程序中定义新的数据类型(我们今天认为这是理所当然的,但在当时并不明显)。你可以通过类型定义的方式定义自己的数据类型,如子界类型、数组类型、记录类型、枚举类型、指针类型和集合类型。类是最重要的用户自定义数据类型,它是面向对象语言功能的一部分,将在本书第二部分介绍。

​ 如果你认为类型构造函数在许多编程语言中都很常见,那么你是对的,但 Pascal 是第一种以正式和非常精确的方式引入这种思想的语言。Object Pascal 仍有一些相当独特的功能,比如子界、枚举和集合的定义,这些将在下面的章节中介绍。第 5 章将介绍更复杂的数据类型构造函数(如数组和记录)。

2.3.1 已命名类型与未命名类型

​ 用户定义的数据类型可以为以后使用的变量命名,也可以直接应用于变量。Object Pascal 的惯例是使用字母 T 前缀来表示任何数据类型,包括类但不限于类。我强烈建议你遵守这一规则,如果你来自 Java 或 C# 背景,即使一开始可能感觉不自然。

​ 为类型命名时,必须在程序的 "type"部分进行(每个单元中可以添加任意多的类型)。下面是几个类型声明的简单示例:

type
          // 子界定义
          TUppercase = 'A'..'Z';
          // 枚举类型定义
          TMyColor = (Red, Yellow, Green, Cyan, Blue, Violet);
          // 集合定义
          TColorPalette = set of TMyColor;

​ 有了这些类型,现在您就可以定义一些变量:

var
       UpSet: TUpperLetters;
       Color1: TMyColor;

​ 在上述情况中,我使用的是命名类型。另一种方法是直接使用类型定义来定义变量,而不使用明确的类型名称,如下面的代码:

var
       Palette: set of TMyColor;

​ 一般来说,应避免使用上述代码中的未命名类型,因为您不能将它们作为参数传递给例程或声明相同类型的其他变量。鉴于语言最终采用的是类型名称等价而非结构类型等价,因此对每种类型使用单个定义确实很重要。另外请记住,通过 uses 语句,可以在任何其他单元的代码中看到单元interface部分的类型定义。

​ 上述类型定义是什么意思?我将为那些不熟悉传统 Pascal 类型结构的人提供一些说明。我还会尽量强调与其他编程语言中相同结构的差异,所以无论如何,你都可能有兴趣阅读下面的章节。

2.3.2 类型别名

​ 正如我们所看到的,Delphi 语言在检查类型兼容性时使用的是类型名称(而不是实际定义)。两个定义相同但名称不同的类型就是两个不同的类型。

​ 在定义类型别名(即基于现有类型的新类型名称)时,部分情况也是如此。令人困惑的是,同样的语法有两种变体,产生的效果却略有不同。请看 TypeAlias 示例中的代码:

type
       TNewInt = Integer;
       TNewInt2 = type Integer;

​ 这两种新类型与 Integer 类型保持赋值兼容(通过自动转换);但是,TNewInt2 类型不会完全匹配,例如,它不能作为引用参数传递给期望使用别名类型的函数:

procedure Test(var N: Integer);
begin
end;

procedure TForm40.Button1Click(Sender: TObject); 
var
  I: Integer;
  NI: TNewInt;
  NI2: TNewInt2;
begin
  I := 10;
  NI := I; // 正常工作
  NI2 := I; // 正常工作
  Test(I);
  Test(NI);
  Test(NI2); // 错误

​ 最后一行会产生错误:

E2033 Types of actual and formal var parameters must be identical

​ 与类型助手一样,Integer 类型助手可用于 TNewInt,但不能用于 TNewInt2。这在后面讨论记录助手时有具体涉及。

2.3.3 子范围类型

​ 子范围(subrange)类型定义了另一个类型范围内的值范围(因此称为子范围)。例如,您可以定义一个从1到10或从100到1000的字符类型的子范围,或者您可以定义一个只包含英文字符的字符类型的子范围,如下所示:

type
       TTen = 1..10;
       TOverHundred = 100..1000;
       TUpperCase = 'A'..'Z';

​ 在子范围的定义中,您无需指定基本类型的名称。您只需提供该类型的两个常量。原始类型必须是序数类型,结果类型将是另一种序数类型。当您将变量定义为子范围时,然后可以将任何在该范围内的值分配给它。以下代码是有效的:

var
       UppLetter: TUpperCase;
begin
       UppLetter := 'F';

​ 但这是无效的:

var
       UppLetter: TUpperCase;
begin
       UppLetter := 'e'; // 编译时错误

​ 上述代码会导致编译错误,“常量表达式违反子范围边界”。如果您改为编写以下代码,编译器将通过编译:

var
         UppLetter: TUpperCase;
         Letter: Char;
begin
         Letter :='e';
         UppLetter := Letter;
end;

​ 在运行时,如果启用了范围检查编译器选项(在 "项目选项 "对话框的 "编译器 "页面),就会如预期一样收到范围检查错误信息。这与我前面描述的整数类型溢出错误类似。

​ 我建议你在开发程序时打开这个编译器选项,这样程序会更健壮,调试起来也更容易,因为如果出现错误,你会得到一个明确的信息,而不是一个不确定的行为。在最终编译程序时,你可以禁用该选项,这样程序运行速度会更快一些。不过 速度的提高几乎可以忽略不计,因此我建议将所有这些运行时检查都打开、 即使是在运行中的程序中。

2.3.4 枚举类型

​ 枚举类型(通常称为 “枚举”)是另一种用户定义的序数类型。在枚举类型中,用户列出的不是现有类型的范围,而是该类型的所有可能值。换句话说,枚举是一个(常量)值列表。下面是一些示例:

type
         TColors = (Red, Yellow, Green, Cyan, Blue, Violet);
         TSuit = (Club, Diamond, Heart, Spade);

​ 列表中的每个值都有一个相关的常量,从 0 开始。对枚举类型的值应用 Ord 函数时,就会得到这个 "基于零 "的值。例如,Ord(Diamond) 返回 1。

​ 枚举类型可以有不同的内部表示法。默认情况下,Delphi 使用 8 位表示法,除非有超过 256 个不同的值,在这种情况下使用 16 位表示法。此外还有 32 位表示法,有时这种表示法对于与 C 或 C++ 库兼容非常有用。

注解: 您可以通过使用 $Z 编译器指令来更改枚举类型的默认表示法,即无论枚举中的元素数量多少,都要求使用较大的表示法。这是一种相当罕见的设置。

域枚举

​ 枚举类型的特定常量值可以被视为全局常量,不同的枚举值之间可能存在命名冲突的情况。这就是语言支持作用域枚举的原因,这是一种您可以使用特定编译器指令 $SCOPEDENUMS 来激活的功能,并且需要您使用类型名称作为前缀来引用枚举值:

// 经典的枚举值
S1 := Club;
// 作用域枚举值
S1 := TSuit.Club;

​ 当引入该功能时,为了避免破坏现有代码,保留了传统的默认编码样式。实际上,域枚举改变了枚举的行为,使其必须使用完全限定的类型前缀来引用。

​ 拥有一个绝对名称来引用枚举值消除了冲突的风险,可以避免只使用枚举的初始前缀从而与其他枚举值区分开,这样即使写起来更长,也使代码更易读。

​ 例如,System.IOUtils 单元定义了此类型:

{$SCOPEDENUMS ON}
type
TSearchOption = (soTopDirectoryOnly, soAllDirectories);

​ 这意味着您不能将第二个值称为 soAllDirectories,而必须使用其完整名称引用枚举值:

TSearchOption.soAllDirectories

​ FireMonkey 平台库也使用了大量域枚举,要求将类型作为实际值的前缀,而较早的 VCL 库一般采用更传统的模式。RTL 是采用了两者的混合体。

注解: Object Pascal 库中的枚举值通常在值的开头使用两个或三个类型的首字母,按惯例使用小写字母,如上例中的搜索选项(Search Options)的 “so”。在使用类型作为前缀时,这似乎有点多余,但考虑到这种方法的普遍性,我认为它不会很快消失。

2.3.5 集合类型

​ 集合类型表示一组值,可用值的列表由集合所基于的序数类型指示。这些序数类型通常是有限的,通常用枚举或子界来表示。

​ 如果我们让子界取值为 1 到 3,用 Pascal 符号写成 1…3,那么基于它的集合的可能取值包括只有 1、只有 2、只有 3、既有 1 又有 2、既有 1 又有 3、既有 2 又有 3、所有三个值或一个值都没有。

​ 变量通常包含其类型范围的可能值之一。另一方面,集合类型变量可以不包含任何值,也可以包含一个、两个、三个或任意多个范围内的可能值。它甚至可以包含所有的值。

​ 下面是一个集合的示例:

type
  TSuit = (Club, Diamond, Heart, Spade);
  TSuits = set of TSuit;

​ 现在,我可以定义一个这种类型的变量,并为其分配一些原始类型的值。要表示集合中的某些值,可以写一个用逗号分隔的列表,并用方括号括起来。下面的代码显示了给变量赋值的情况,包括几个值、一个值和一个空值:

var
	Cards1, Cards2, Cards3: TSuits;
begin
	Cards1 := [Club, Diamond, Heart];
	Cards2 := [Diamond];
	Cards3 := [];

​ 在 Object Pascal 中,集合通常用于指示多个非排他性标志。例如,基于集类型的值是字体样式。可能的值表示粗体、斜体、下划线和删除线字体。当然,相同的字体可以是斜体和粗体,没有属性,也可以具有全部属性。因此,它被声明为一个集合。

​ 您可以在程序代码中为该集合赋值,具体方法如下:

Font.Style := []; // No style
Font.Style := [fsBold]; // Bold style only
Font.Style := [fsBold, fsItalic]; // Two styles active

集合运算符

​ 我们已经看到,集合是一种非常具有Pascal风格的用户自定义数据类型。这就是为什么集合运算符值得特别介绍的原因。它们包括并(+)、差(-)、交(*)、成员测试(in)以及一些关系运算符。

​ 要将元素添加到集合中,可以将该集合与另一个仅包含所需元素的集合合并。下面是一个与字体样式相关的示例:

// Add bold
Style := Style + [fsBold];
// Add bold and italic, but remove underline if present
Style := Style + [fsBold, fsItalic] - [fsUnderline];

​ 作为替代方法,您可以使用标准的Include和Including过程,这要有效得多(但不能与set类型的组件属性一起使用):

Include(Style, fsBold);
Exclude(Style, fsItalic);
  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
第一部分: 感受Delphi 7的可视化开发环境,并制作了若干简单的实例,着重从开发步骤的角度引导读者完成实例的制作,并详细讲解了开发过程中遇到的关于控件的和语言的相关问题,使得读者对Delphi 7的开发流程有初步的了解。 第1 主要介绍Delphi 7的安装以及对可视化开发环境的熟悉。 第2 通过手把手的几个实例的讲解,引导读者进行“搭积木”式的实例开发,从中感受使用Delphi 7开发应用程序的简洁和高效,并进一步掌握可视化开发环境的使用。 第二部分: 第3和第4分别对使用Delphi 7进行系统开发的语言语法以及开发中有关错误的调试技巧进行了讲解,使读者从理性上认识Delphi 7开发并熟练掌握程序调试技巧。 第3 主要讲解Delphi 7开发应用程序所使用的语言——Object Pascal语言。除了概念上的阐述外,绝大部分的基本知识将从实例的制作中获取,避免了枯燥的讲解。 第4 介绍用Delphi 7进行软件开发过程中经常遇到的错误以及解决办法。本摈弃了传统的Delphi教程中枯燥的调试工具操作讲解和对错误理论的讲解,而是集中介绍了开发过程中最经常和最有效的调试手段,并简要介绍了错误的类型和解决办法。同样的,本以实例的形式把相关的知识融入其中。 第三部分: 接下来的第5、第6和第7对使用Delphi 7进行系统的软件开发进行了讲解,使读者在更高的层次上看清Delphi 7开发是进行设计而不仅仅是进行编码。 第5 主要介绍如何使用Delphi 7开发出美观大方的应用程序以及开发过程中的相关原则。 第6 主要介绍了有关图形控件方面的运用技巧。 第7 主要介绍了有关数据库开发的相关知识,用一个简单的例子讲解了数据库的查询、删除、插入和更新等基本功能。 第四部分: 第8是本书的最后一部分。该部分通过一个实例完成对本书基本知识的总结与复习。这里既有美观的界面设置,也有逻辑的代码设计,还有整个系统开发的分析说明。通过这一力求读者从整体上把握软件开发的步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值