1

当您定义一个类(或带有方法的对象或记录)时,我只是想知道数据字段是否应该出现在方法字段之前是否有任何约定?

我的意思是像下面这个简单的例子中两种情况之间的区别。(编辑以使具体问题更清楚)

type TWhatever = Object
      private
        a,b : Integer;
      public
        procedure SetWhatever(x,y :Integer);
        procedure ShowWhatever;
      end;

相对

type TWhatever = Object
      public
        procedure SetWhatever(x,y :Integer);
        procedure ShowWhatever
      private
        a,b : Integer;
     end;

我一直使用首先列出数据字段的约定,但老实说,我并不认为它有任何区别。但是最近我遇到了一些不起作用的代码(无法编译),除非首先列出数据字段。

我在这里记录了这个案例:Delphi 2006 wont allow const parameters of type record within record method?

总结是,如果您将数据字段放在方法之后,那么如果对象有任何方法尝试将给定对象作为“const”参数传递,则代码将无法编译。例如像这样的东西不会编译:

procedure TWhatever.SomeMethod( const : w1: TWhatever);

显然它将在更高版本的 Delphi 上编译,但我已经在 Delphi7 和 Delphi 2006 上对其进行了测试,但两者都无法编译。

仅当您执行所有三件事时才会出现问题(数据字段最后,在方法中将对象作为参数传递,为此参数使用 const 关键字)。因此,您可以通过从参数中删除 const 关键字或将数据字段放在方法之前来解决问题。

无论如何,这个问题让我想知道这里是否有要遵循的约定?

编辑我添加了这个来给出一个特定的代码示例

以下程序无法在 D7 和 D2006 上编译:

program bugtest;
{$APPTYPE CONSOLE}
uses SysUtils;

type Tob = object
   public
    procedure setOb(const a,b: integer);
    procedure addToOb(const ob1: Tob);
    procedure printOb;
   private
    x,y : Integer;
   end;

procedure Tob.setOb(const a,b: integer);
begin
  x:=a; y:=b;
end;

procedure Tob.addToOb(const ob1: Tob);
begin
  x:=x+ob1.x; y:= y+ob1.y;
end;

procedure Tob.printOb;
begin
  writeln(x,' ',y);
end;

var r1,r2: Tob;
begin
  r1.setOb(2,3);
  r2.setOb(10,100);
  r1.addToOb(r2);
  r1.printOb;
  r2.printOb;
  readln;
end.

以下程序可以在 D7 和 D2006 上完美编译和运行:

program bugtest;
{$APPTYPE CONSOLE}
uses SysUtils;

type Tob = object
   private
    x,y : Integer;
   public
    procedure setOb(const a,b: integer);
    procedure addToOb(const ob1: Tob);
    procedure printOb;
   end;

procedure Tob.setOb(const a,b: integer);
begin
  x:=a; y:=b;
end;

procedure Tob.addToOb(const ob1: Tob);
begin
  x:=x+ob1.x; y:= y+ob1.y;
end;

procedure Tob.printOb;
begin
  writeln(x,' ',y);
end;

var r1,r2: Tob;
begin
  r1.setOb(2,3);
  r2.setOb(10,100);
  r1.addToOb(r2);
  r1.printOb;
  r2.printOb;
  readln;
end.

Delphi 7 版本为:Delphi Personal Version 7.0 build 4.453

D2006版本是:Borland Delphi for MS Windows。版本 10.0.2288.42451,更新 2

编译器截图

4

2 回答 2

4

根据编译器,字段声明在任何类 section中的任何方法声明之前。一个类可能有任意数量的部分,即使具有相同的可见性。

例如:

type
  TMyObject = class
  private
    FTest: Integer;
    procedure A();
  private
    FOtherTest: Integer;
    procedure B();
  end;

编译,你可以争论是否FOtherTest在之后procedure A()

由于这是由编译器强制执行的,因此无法以其他方式进行,因此我不确定是否将其称为约定,但必须如此。

另一方面,

type
  TMyObject = class
  private
    procedure A();
    FTest: Integer;
  end;

不编译,报错:

E2169 在方法或属性之后不允许字段定义

是否将它作为任何类型的参数传递都没关系。根据我的经验,如果您只是在方法之后声明任何字段,则会引发错误。

编辑

编辑问题后,如果对象作为 const 参数传递给例行公事。

我设法在 VM 中运行我非常旧的D7,并且这个示例编译正常:

在此处输入图像描述

它使用objectorclass声明进行编译,并且由于records不支持该版本的方法,显然记录声明无法编译。

编辑2

如果对象作为 const 参数传递给方法,它也会编译(不管是同一个对象的方法还是其他方法)

在此处输入图像描述

于 2013-03-04T01:02:18.337 回答
1

我认为公平地说,有一个广泛使用的约定。通常一个类声明会这样安排:

TMyClass = class
  // published declarations, managed by form designer
  Component1: TMyComponent;
  procedure MyEventHandler(Sender: TObject);
private
  FMyPrivateField: Integer;
  procedure MyPrivateMethod;
protected
  FMyProtectedField: Integer;
  procedure MyProtectedMethod;
public
  FMyPublicField: Integer;
  procedure MyPublicMethod;
published
  FMyPublishedField: Integer;
  procedure MyPublishedMethod;
end;

换句话说,这些部分将首先按照表单设计者的声明进行组织,然后按照增加可见性的顺序进行组织。

有趣的是,其他语言也使用了其他约定。例如,查看Google C++ 样式指南,其中部分的顺序与上述相反。

就我个人而言,我更愿意先看到公开声明。在阅读已经存在的代码时,我通常会以自上而下的视角阅读。通常在编写代码时,我发现自上而下的方法是有益的。

查看我的TComplex记录,声明按原样排序是有原因的。我有许多类似的记录,其中一些是变体记录。例如:

type
  TVector = record
  public
    class operator Negative(const V: TVector): TVector;
    class operator Equal(const V1, V2: TVector): Boolean;
    class operator NotEqual(const V1, V2: TVector): Boolean;
    class operator Add(const V1, V2: TVector): TVector;
    class operator Subtract(const V1, V2: TVector): TVector;
    class operator Multiply(const V: TVector; const D: Double): TVector;
    class operator Multiply(const D: Double; const V: TVector): TVector;
    class operator Divide(const V: TVector; const D: Double): TVector;
    function IsZero: Boolean;
    function IsNonZero: Boolean;
    function IsUnit: Boolean;
    function Max: Double;
    function Sum: Double;
    function Mag: Double;
    function SqrMag: Double;
    function MagNormalComponent: Double;
    procedure MakeAxisymmetric;
    class function SNaN: TVector; static;
    function GetHashCode: Integer; inline;
  public
    case Boolean of
    False:
      (X, Y, Z: Double);
    True:
      (a: array [1..3] of Double);
  end;

现在,记录的变体部分必须出现在最后。否则会出现编译错误。所以这张唱片根本没有选择。建立了公约TVector和相关记录后,TComplex简单地效仿。

于 2013-03-04T09:16:48.393 回答