简体   繁体   English

Delphi 2010中记录与类的类方法

[英]Class methods in Record vs Class in Delphi 2010

I just started to play with the new unit in Delphi 2010 IOUtils.pas , and I found they put all the methods inside Records(TFile, TPath, TDirectory) as class functions and procedure. 我刚开始使用Delphi 2010 IOUtils.pas的新单元,我发现他们将所有方法放在Records(TFile, TPath, TDirectory)作为类函数和过程。

Is there any benefits of doing that inside records instead of classes? 在记录而不是类中执行此操作有什么好处吗? In both cases there's no need for any variables or instance, but I'm not sure if there any real benefits regarding memory consuming or performance improvement. 在这两种情况下都不需要任何变量或实例,但我不确定在内存消耗或性能改进方面是否有任何实际好处。

Class methods in records are used to group different methods into a common namespace. 记录中的类方法用于将不同的方法分组到公共名称空间中。 Thus you can have similar named methods for different purposes. 因此,您可以为不同的目的使用类似的命名方法。 For an example in IOUtils.pas look at the Exists function available in TFile and in TDirectory. 有关IOUtils.pas中的示例,请查看TFile和TDirectory中提供的Exists函数。 The older approach was to have distinct function names for FileExists and DirectoryExists (which the implementations actually call). 较旧的方法是为FileExists和DirectoryExists(实现实际调用)具有不同的函数名称。

While class methods inside classes can be used in the same way, they can in addition have another goal: they can be virtual. 虽然类中的类方法可以以相同的方式使用,但它们还可以有另一个目标:它们可以是虚拟的。 Called from a class variable this can lead to different implementations depending on the current content of that variable. 从类变量调用,这可能导致不同的实现,具体取决于该变量的当前内容。 This is not possible for records. 这不适用于记录。 As a consequence class methods in records are always static. 因此,记录中的类方法始终是静态的。

I would say that the first question to ask is "Why put such functions inside either a record or a class?" 我想说的第一个问题是“为什么把这些函数放在记录类中?” in the first place. 首先。

The use of record methods as in IOUtils is in many cases largely arbitrary in Delphi and in part reflects a fetish with following conventions in other languages that are not directly relevant in Delphi. IOUtils中使用记录方法在很多情况下在Delphi中基本上是任意的,并且部分反映了在Delphi中与其他语言无关的具有以下约定的迷信。

In some languages (Java and .NET?) "first class" functions are not supported. 在某些语言(Java和.NET?)中,不支持“第一类”功能。 That is, procedures and functions cannot exist independently of any container class. 也就是说,程序和函数不能独立于任何容器类存在。 Hence in those languages if you have functions and procedures they must be contained in a class. 因此,在这些语言中,如果您有函数和过程,则必须将它们包含在类中。 If they do not operate on member data then obviously they are declared as class methods to avoid having to construct an instance merely to call what is in fact a classLESS function. 如果它们不对成员数据进行操作,那么显然它们被声明为类方法,以避免仅仅为了调用实际上是classLESS函数而构造一个实例。

ie

TDirectory.Exists(s) and TFile.Exists(s) are syntactic alternatives to DirectoryExists(s) and FileExists(s) . TDirectory.Exists(s)TFile.Exists(s)DirectoryExists(s)FileExists(s)的语法替代品。

In Delphi first class functions are supported and there is therefore no real reason to use classes or records to discriminate between functions, except as a way of facilitating ease of reference for a developer when writing code and using IDE assistance to browse for available methods. 在Delphi 中,支持第一类函数因此没有真正的理由使用类或记录来区分函数,除非是在编写代码和使用IDE辅助浏览可用方法时便于开发人员参考的方法。

Without a container record/class you have to know that there is a function called " DirectoryExists() ". 没有容器记录/类,您必须知道有一个名为“ DirectoryExists() ”的函数。 With a container record, you only have to know that there is a type TDirectory , and the IDE (via code completion suggestions) can then present all of the "methods" available for TDirectory operations. 使用容器记录,您只需知道存在类型TDirectory ,然后IDE(通过代码完成建议)可以呈现可用于TDirectory操作的所有“方法”。

However, in Delphi there is an alternative made possible by the support for first class functions. 但是,在Delphi中,通过支持一流功能可以实现另一种选择。 Rather than using arbitrary and artificial "containers", you (or rather the authors of IOUtils ) could instead have chosen to use the existing container - a Delphi "unit": 您(或者更确切地说是IOUtils的作者)可以选择使用现有容器 - 一个Delphi“单元”,而不是使用任意和人为的“容器”:

  unit IOUtils.Directory;

  interface

  function Exists(s): Boolean;

and

  unit IOUtils.File;

  interface

  function Exists(s): Boolean;

This still enjoys the benefits of IDE support in the form of code completion suggestions and also provides the namespace separation required to support identical function names operating on different things. 这仍然以代码完成建议的形式享受IDE支持的好处,并且还提供了支持在不同事物上运行的相同功能名称所需的命名空间分离。

The downside of this approach (and it is a big one) is that where a unit uses both IOUtils.File and IOUtils.Directory , it must qualify one or other function name to avoid confusion, but this qualification could be omitted, which is why it is so dangerous: 这种方法的缺点(并且它是一个很大的)是一个单元同时使用IOUtils.FileIOUtils.Directory ,它必须限定一个或其他函数名称以避免混淆,但这个限定可以省略,这就是为什么它太危险了:

  uses
    IOUtils.File,
    IOUtils.Directory;
  ..
  begin
    if Exists(s) then  // << tests existence of a directory
    ..
    if IOUtils.File.Exists(s) then  // << ensure we test for a file
    ..
    if IOUtils.Directory.Exists(s) then  // << ensure we test for a directory
  end

This may also be an advantage of course, if your unit contains code that ONLY works with directories, it will use only IOUtils.Directory and need not qualify and repeat that fact anywhere other than in the uses clause. 当然,这也可能是一个优势,如果您的单元包含仅适用于目录的代码,它将仅使用IOUtils.Directory,并且无需限定并在uses子句以外的任何地方重复该事实。

A potential and very REAL danger here of course is that in future if the unit is extended and subsequently starts using IOUtils.File , then this could easily lead to scoping errors on any unqualified references. 当然,潜在且非常真实的危险在于,将来如果单元被扩展并随后开始使用IOUtils.File ,那么这很容易导致任何不合格的引用上的作用域错误。

Which approach you prefer in your code will depend largely on personal preference and the possibility for collisions in scoping errors where you have similar/identically named or semantically equivalent functions operating on different entities. 您在代码中偏好哪种方法在很大程度上取决于个人偏好以及在范围错误中发生冲突的可能性,其中您具有在不同实体上运行的类似/相同命名或语义上等效的函数。

For those reasons, the design choice in IOUtils at least is understandable and arguably a good one, tho ymmv. 出于这些原因, IOUtils中的设计选择至少是可以理解的,并且可以说是一个很好的选择,因为ymmv。

But in other cases, if all you are seeking is a way to group related functions that are not at risk of confusion/collision with similar functions, then the use of an artificial and arbitrary container is superfluous. 但在其他情况下,如果您正在寻找的是一种将相关函数分组的方法,这些函数不会出现与类似函数混淆/冲突的风险,那么使用人工和任意容器是多余的。 The unit that the functions must reside in may itself provide all the grouping behaviour you need. 函数必须驻留的单元本身可以提供您需要的所有分组行为。

As for why a record rather than a class... 至于为何记录而不是班级...

I suspect that there may be a small static memory benefit to using a record rather than a class when fabricating these arbitrary containers. 我怀疑在制作这些任意容器时使用记录而不是类可能会有一点静态内存的好处。 I imagine that a class has additional overhead, tho this is going to be relatively small and static in nature (ie a bit more overhead for each class, but not related to the number of times it's class methods are used). 我认为一个类有额外的开销,这本质上是相对较小和静态的(即每个类的开销稍微多一点,但与使用类方法的次数无关)。

The static class methods are just a syntactic sugar - you can always implement them as ordinary procedures/functions. 静态类方法只是一种语法糖 - 你总是可以将它们作为普通的程序/函数来实现。

If a class method is implemented inside a record, there is less overhead. 如果在记录中实现了类方法,则开销较小。 Any class declaration requires an additional structure for class info in memory. 任何类声明都需要在内存中为类信息提供额外的结构。 Records does not have the structure. 记录没有结构。

IMO one would need a very good reason to use records over classes with static methods. IMO需要一个很好的理由来使用静态方法对类进行记录。 Like for TValue when speed matters. 当速度很重要时,就像TValue一样。 For instance it just turns out that TDirectory.CreateDirectory() just fails for UNC Paths cause TPath.DriveExists() fails for an UNC path. 例如,事实证明TDirectory.CreateDirectory()只是UNC路径失败导致TPath.DriveExists()失败的UNC路径。 (D2010) If it were a class i would just write a helper class implementing my own TDirectory.CreateDirectoryEx(). (D2010)如果它是一个类,我会编写一个实现我自己的TDirectory.CreateDirectoryEx()的辅助类。 Since its a record I can't and either copy some code from IOUtils or write my own one. 由于它是一个记录,我不能从IOUtils复制一些代码或编写我自己的代码。 In any case I endup with code-redundancy. 在任何情况下,我都会使用代码冗余。 So from the viewpoint of exensible code choosing records is a bad idea. 因此从可行的代码选择记录的角度来看是一个坏主意。 (I actually still wonder why the VCL-Team decided not to consider extensibility. I mean compare the VCL with a DevEx's QuantumGrid under this aspect. There are lightyears in between) (我实际上仍然想知道为什么VCL团队决定不考虑可扩展性。我的意思是在这方面将VCL与DevEx的QuantumGrid进行比较。两者之间有光年)

Ok, I just found out that you can actually write a helper for a record: 好的,我刚刚发现你实际上可以为一条记录写一个帮手:

TDirectoryHelper = record helper for TDirectory

end;

But it doesn't help much since you can't access any private static method from TDirectory 但它没有多大帮助,因为您无法从TDirectory访问任何私有静态方法

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM