为什么我们应该使用类而不是记录,或者反过来?

28

我已经使用Delphi相当长的一段时间了,但是与其说是从CS背景中学习的,不如说是在工作中“摸索” - 大多数是从我的老板那里学到的,再加上从网络、用户指南、示例等方面收集的碎片知识。

现在我的老板是老派人士,他使用Pascal开始编程,并没有必要随时跟进 Delphi 的最新变化。

最近,我一直在想我们的核心技术是否“错误”。

我们的大多数应用程序都与MySQL交互。通常,我们会创建一个record来存储从数据库读取的数据结构,并将这些记录存储在TList中。通常,我们会有一个单元来定义应用程序中各种记录以及种子和读取记录的函数和过程。我们不使用记录过程,例如在这里概述的那样。

在查看一些示例后,我开始思考是否应该使用classes而不是记录,但我很难找到强有力的指导。

我们处理的是用户信息: 姓名、出生日期、事件、事件类型等。或者是Timesheet信息: 工时、工作等。

1个回答

46
大的区别在于记录是值类型,而类是引用类型。简而言之,这意味着:
1. 对于值类型,在使用赋值操作符“a := b”时,会创建一个副本。有两个不同的实例,“a”和“b”。
2. 对于引用类型,在使用赋值操作符“a := b”时,两个变量都指向同一个实例。只有一个实例。
这样做的主要后果是当您编写“a.Field := 42”时会发生什么。对于值类型,即记录,赋值“a.Field”将更改“a”中成员的值,但不会更改“b”的值。这是因为“a”和“b”是不同的实例。但对于类,由于“a”和“b”都指向同一个实例,因此执行“a.Field := 42”后,可以安全地断言“b.Field = 42”。
没有硬性规定说你应该总是使用值类型或总是使用引用类型。两者都有其适用的场合。在某些情况下,使用一种类型会更好,在其他情况下使用另一种类型会更好。本质上,这个决定总是取决于你想让赋值运算符意味着什么。
你有一个现有的代码库,并且熟悉它的程序员已经做出了特定的选择。除非你有强制性的理由要切换到使用引用类型,否则进行更改几乎肯定会导致缺陷。这些缺陷既存在于现有代码中(切换到引用类型会改变赋值运算符的含义),也存在于你将来编写的代码中(你和你的同事已经对特定上下文中赋值运算符的含义产生了直觉,如果你切换,这种直觉将会破裂)。
此外,你声明你的类型不使用方法。一个只包含数据而没有与之相关联的方法的类型很可能最好由值类型表示。我不能确定,但我的直觉告诉我原始开发人员做出了正确的选择。

+1 完全与我的第一直觉相反,但非常务实,所以最终我不得不同意。然而,OP应该注意到由于赋值可能会导致性能下降的可能性。例如:将记录参数作为“const”传递应该是默认设置,但这可能会破坏应用程序,我们又回到了尝试务实的状态<g>。 - Lieven Keersmaekers
@LievenKeersmaekers 是的,我确实考虑过讨论参数传递,但出于简单起见选择不进行讨论。你可以将按值传递的参数视为赋值的等价形式。参数是局部变量。按值传递会复制值或引用,取决于参数的类型。因此,对于赋值所了解的知识同样适用于按值传递的参数。 - David Heffernan
谢谢。我的直觉告诉我最好保持现状,只是试图向前规划...据我了解,记录方法很有吸引力,因为每种记录类型通常都会有3/4个相关的方法(加载、保存、查找等)。 - Dan Kelly
6
@DanKelly:recordsclasses 的另一个区别在于内存管理。当您声明一个 record 类型的变量时,编译器会使用堆栈(stack)分配其内存。当该变量超出作用域后,其内存会自动管理(释放)。而当您声明一个 class 类型的变量时,您只获取了一个引用,并且您需要调用其构造函数来分配您将要操作的实例。当您完成使用对象后(暂时),您有责任通过直接或间接地调用其析构函数来销毁该实例。 - AlexSC
1
@Alex 在这种情况下,它们存储在TList中,因此它们也是堆分配的。 - David Heffernan
显示剩余6条评论

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