2012年10月3日水曜日

record / class helper のスコープについて

record helper / class helper のスコープ(適用順序)について調べみます。
同じユニット内に次のような2つの helper があった場合

type
  TIntegerHelperA = record helper for Integer
    function foo: String;
  end;

  TIntegerHelperB = record helper for Integer
    function bar: String;
  end;

function TIntegerHelperA.foo: String;
begin
  Result := 'A';
end;

function TIntegerHelperB.bar: String;
begin
  Result := 'B';
end;

begin
  Writeln(1000000.foo);  // コンパイルエラー
  Writeln(1000000.bar);  // OK
end.

直近の helper が有効になります。
メソッド名が違っても関係ありません。

それは helper が別のユニットにあったとしてもです。

unit Unit1;

interface

type
  TIntegerHelperA = record helper for Integer
    function foo: String;
  end;

(略)
unit Unit2;

interface

type
  TIntegerHelperB = record helper for Integer
    function bar: String;
  end;

(略)

uses するとき Unit2 を後にすると、TIntegerHelperA がエラーに。
uses
  Unit1, Unit2;

begin
  Writeln(1000000.foo);  // コンパイルエラー
  Writeln(1000000.bar);  // OK
end.

Unit1 を後にすると、TIntegerHelperB がエラーになります。
uses
  Unit2, Unit1;

begin
  Writeln(1000000.foo);  // OK
  Writeln(1000000.bar);  // コンパイルエラー
end.
同じメソッド名を持つ helper が定義してあると、直近の helper メソッドが呼ばれます。
意図していないメソッドが呼ばれる事が無いよう注意が必要です。

では、次のような場合はどうなるでしょう?

type
  TTest = class(TObject);

  TObjectHelper = class helper for TObject
    function Hello: String;
  end;

  TTestHelper = class helper for TTest
    function Hello: String;
  end;

{ TObjectHelper }

function TObjectHelper.Hello: String;
begin
  Result := 'Hello, TObjectHelper';
end;

{ TTestHelper }

function TTestHelper.Hello: String;
begin
  Result := 'Hello, TTestHelper';
end;

var
  Test: TTest;
begin
  Test := TTest.Create;
  try
    Writeln(Test.Hello);
  finally
    Test.Free;
  end;
end.

この場合、コンパイルエラーにはなりません。
helper は、1つの class / record につき1つ有効なので、継承元と継承先のクラスは別個に helper を持てるからです。

上の場合、出力される値は

Hello, TTestHelper

となります。
では、さらに下記の様にキャストした場合は、どうなるでしょう?

var
  Test: TTest;
begin
  Test := TTest.Create;
  try
    Writeln(TObject(Test).Hello); // TObject にキャスト
  finally
    Test.Free;
  end;
end.

結果は

Hello, TObjectHelper

と出力されます。 これらの事から helper はコンパイル時に決定される機構であることが判ります。

ヘルプにも
ヘルパは、識別子を解決するときに、コンパイラが使用するスコープの範囲を広げる機能です (from DocWiki)
と書いてありますしね。

0 件のコメント:

コメントを投稿