理解联合类型

3
在Pascal中,可以声明联合类型:
AnimalType = (Dog, Cat);

Animal = record
  name: string;
  case myType: AnimalType of
    Dog: (weight: Integer);
    Cat: (age: Integer);
end;

然而,违反 case的契约非常容易:

var
  a: Animal;
begin
  a.name := 'Kittie';
  a.myType := Cat;
  a.weight := 10; // There is no weight for cats!

  writeln(a.age); // Prints 10
end.

这个示例存在语义错误,但编译器成功地进行了类型检查。此外,在运行时也没有出现错误。

那么,case块只是为了文档目的而存在吗?


当您声明此类变量时,两个变体成员都会被分配,因此不会出现错误。但这并不是为了“文档目的”,您只需小心在这种情况下想要使用什么即可。尽管如此,如果编译器拒绝这样的代码,那就太好了。 - TLama
我不理解case块的目的。它是否会对任何东西产生影响? - ZhekaKozlov
1
唯一的目的是让体重和年龄共享同一内存位置。如何区分语义差异取决于实现方式。 - LU RD
@LURD 是的,但是 case 分支会影响什么吗?如果我交换 DogCat,会有什么不同吗? - ZhekaKozlov
如果您在case分支中更改顺序,不会有任何不同。 - LU RD
1
你说的是ISO标准的Pascal还是Borland的衍生版本? - Marco van de Voort
2个回答

2
你的问题的简短回答是,“变体记录中的case块不仅仅是用于文档目的”。我这么说是因为,尽管你使用的Pascal实现没有检测到程序访问了非活动变体的事实,但其他实现确实会检测到此错误。
对你的问题的详细回答如下。
许多刚开始学习Pascal的人并不知道Pascal语言有两种主要的版本。一种是ISO 7185标准Pascal(或者简称为标准Pascal),另一种是Turbo/Borland Pascal(最流行的变体)。所以,我给你两个关于你的问题“那么,case块只是用于文档目的吗?”的答案。
标准Pascal的回答
标准Pascal将错误定义为“程序违反本国际标准要求,处理器允许未检测到的情况”。因此,是的,你提供的程序包含一个错误,而且你正在使用的处理器(即Pascal实现)没有检测到它,但其他实现将会检测到它,所以变体记录中的case块实际上具有功能目的。
Turbo/Borland的回答
就Turbo/Borland Pascal版本而言,我不知道它们是否都不能检测到此错误,但即使它们都不能检测到,最好将其视为它们未检测到的错误,而不是仅仅用于文档目的。说某些东西仅仅是用于文档目的,对我来说听起来像是说它从未被设计成具有功能性。

0
一个union可以让一个变量拥有多个类型。在你的例子中,weightage 存储在内存的相同位置。
如果你想要一个“类型安全”的union,你应该使用继承。

这是一个糟糕的答案。联合类型存在,因此多个类型可以存在于同一个盒子中。字符串和整数的联合是一种类型,它可以具有值"One"或1。Pascal有一个奇怪的实现(并且为其辩护,它是最早的之一)。但是,这个概念既不类似于继承,也不能与之互换。您无法使用继承实现联合类型,也无法使用联合类型实现继承。但是,您可以制作类似于它的行为的东西。 - Shayne
值得注意的是,有些人使用联合类型作为一种装箱形式。因此,允许未初始化的字符串实际上是String和Null的联合体(例如,在Crystal中,您将其定义为(String | Null),在编译时进行静态分析会提醒您,在将其传递给任何不能接受null的内容之前,请将其装箱为一个带有null检查的字符串。 - Shayne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接