在方法之前或之后是否有任何数据字段约定? (在班级或对象或记录中)

时间:2013-03-04 00:50:56

标签: delphi

当你定义一个Class(或一个带有方法的Object或Record)时,我只是想知道是否有关于数据字段是否应该出现在方法字段之前的约定?

我的意思是在以下简单例子中两个案例之间的区别。 (编辑以使特定问题更清晰)

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上进行了测试,并且它都不会编译。

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

无论如何,这个问题让我想知道这里是否有一个惯例?

EDITED我添加了这个以提供特定的代码示例

以下程序无法在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,Update2

Screenshot of compiler

2 个答案:

答案 0 :(得分:4)

根据编译器,字段声明在任何class 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方法或属性后不允许字段定义

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

修改

在编辑问题之后,OP说如果在具有方法的部分之后在不同的部分中声明了字段,则在Delphi 7或2006中不会编译对象(不同于类) ,如果对象作为const参数传递给例程。

我设法在VM中运行我的非常旧的 D7,这个例子编译好了:

enter image description here

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

EDIT2

如果对象作为const参数传递给方法(无论是否是同一个对象或其他对象的方法),它也会编译。

enter image description here

答案 1 :(得分: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++ style guidelines,其中部分的顺序与上述相反。

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

查看我的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就会紧随其后。