1

クラス(またはメソッドを持つオブジェクトまたはレコード)を定義するとき、データフィールドをメソッドフィールドの前に表示する必要があるかどうかについての規則があるかどうか疑問に思っていましたか?

次の簡単な例の2つのケースの違いのように意味します。(特定の問題を明確にするために編集)

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は、recordメソッド内でrecord型のconstパラメーターを許可しませんか?

要約すると、メソッドの後にデータフィールドを配置すると、オブジェクトに「const」パラメーターとして指定されたオブジェクトを渡そうとするメソッドがある場合、コードはコンパイルされません。たとえば、次のようなものはコンパイルされません。

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

どうやらそれはDelphiの新しいバージョンでコンパイルされるようですが、私はDelphi7とDelphi 2006の両方でテストしましたが、どちらでもコンパイルされません。

この問題は、3つすべてを実行した場合にのみ発生します(データフィールドが最後になり、オブジェクトをメソッドのパラメーターとして渡し、このパラメーターに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.

Delphi7のバージョンは次のとおりです。DelphiPersonalバージョン7.0ビルド4.453

D2006のバージョンは次のとおりです。MSWindows用のBorlandDelphi 。バージョン10.0.2288.42451、Update2

コンパイラのスクリーンショット

4

2 に答える 2

4

コンパイラによると、フィールド宣言は、クラス セクション内のメソッド宣言よりも前に行われます。同じ可視性であっても、クラスには任意の数のセクションが存在する場合があります。

例えば:

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 パラメータとして渡された場合、フィールドがメソッドを持つセクションの後に別のセクションで宣言されている場合、OP はオブジェクト(クラスとは異なる) が Delphi 7 または 2006 でコンパイルされないことを示しますルーチン。

非常に古いD7 を VM で実行することができました。この例は正常にコンパイルされます。

ここに画像の説明を入力

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 に答える