インライン変数宣言
10.3 の Delphi 言語では、ローカル変数の宣言においてさらなる柔軟性が提供されています。 これまで、伝統的な Pascal 言語の規約に従い、すべての変数は、関数、プロシージャ、メソッドのコードの開始前に記述される、var ブロックで行われる必要がありました。
procedure Test;
var
I: Integer;
begin
I := 22;
ShowMessage (I.ToString);
end;
新しいインライン変数宣言の構文により、コード ブロック内で直接変数を宣言できるようになりました(また、通常通り、複数のシンボルも可):
procedure Test;
begin
var I: Integer;
I := 22;
ShowMessage (I.ToString);
end;
procedure Test2;
begin
var I, K: Integer;
I := 22;
K := I + 10;
ShowMessage (K.ToString);
end;
一部に限定された変更のように感じられるかもしれませんが、この変更には複数の効果があります。 1 つは、宣言と初期化が 1 つの文の中で行われるということです。 2 つめは、複雑なコード ブロック内で必要に応じて、スコープを限定して変数を宣言できるという点です(変数は、その宣言した位置からのみ可視になるため、変数を宣言しておく必要はなく、コードの一部に対しては初期化されません)。
インライン変数のスコープ
3 つの効果は、宣言が第2レベルの begin-end ブロック内でも宣言することができ、スコープはそのブロック内に限定できるという点です。
procedure Test; // declaration and initialization in a single statement
begin
var I: Integer := 22;
ShowMessage (I.ToString);
end;
procedure Test1; // multiple inline declarations (symbols declared when used)
begin
var I: Integer := 22;
var J: Integer;
J := 22 + I;
var K: Integer := I + J;
ShowMessage (K.ToString);
end;
procedure Test2; // scope limited to local block
begin
var I: Integer := 22;
if I > 10 then
begin
var J: Integer := 3;
ShowMessage (J.ToString);
end
else
begin
var K: Integer := 3;
ShowMessage (J.ToString); // COMPILER ERROR: “Undeclared identifier: ‘J’”
end;
end;
上のコード スニペットにみられるよう、begin-end ブロックで宣言された変数は、特定のブロック内でのみ可視になり、ブロックが終了した後には見えなくなります。 if 文の終わりでは、J と K は可視ではなくなっています。
効果は、可視性に限定されません。 管理されている変数(インターフェイスのリファレンスやレコードなど)は、プロシージャやメソッドの終わりではなく、ブロックの終わりで適切にクリーンアップされます:
procedure Test99;
begin
// some code
if (something) then
begin
var Intf: IInterface = GetInterface; // Intf.AddRef
var MRec: TManagedRecord = GetMRecValue; // MRec.Create + MRec.Assign
UseIntf(Intf);
UseMRec(MRec);
end; // Intf.Release and MRec.Destroy are implicitly called at end of scope
// more code
end; // no additional cleanup
インライン変数の型推論
さらに、コンパイラはいくつかの状況下で、int 行宣言の場所で、変数の型を、それに割り当てられる値の型を見ることで、推測できるようになりました。
procedure Test;
begin
var I := 22;
ShowMessage (I.ToString);
end;
右辺の値式の型(つまり、:= の後にくるもの)は、その変数の型を判別するために解析されます。 データ型によっては、上の例での、数値 22(ShortInt)が Integer に拡張されているように、より大きい型へ「拡張」されます。 一般的なルールと同様、普遍の式の型が整数型でかつ 32 ビットより小さい場合、変数は 32 ビット整数として宣言されます。 特定の、より小さい数値型を望む場合には、明示的な型を使用することができます。
また、値の型なしで宣言できるのは、1 つの識別子のみです(一般的な変数宣言やインライン宣言とは異なります)。
この機能により、Interger や文字列に対しては、2-3 のキーストロークの手間を省く一方で、ジェネリック型のインスタンスのような複合型の場合では、変数の型推論はかなり便利なものとなります。 以下のコード スニペットでの推論される型は、変数 MyDictionary に対しては “TDictionary<string, Integer>”、変数 APair に対しては “TPair<string, Integer>” です。
procedure NewTest;
begin
var MyDictionary := TDictionary<string, Integer>.Create;
MyDictionary.Add ('one', 1);
var APair := MyDictionary.ExtractPair('one');
ShowMessage (APair.Value.ToString)
end;
インライン定数
変数のほかに、定数値の宣言もインライン化することができるようになりました。 これは、型が推論される場合(長いこと定数に対してはこの機能は利用可能でした)、型定数や型なし定数に適用できます。 シンプルな例は以下のとおりです。
const M: Integer = (L + H) div 2; // single identifier, with type specifier
const M = (L + H) div 2; // single identifier, without type specifier
変数宣言を伴う for ループ
インライン変数宣言の利点を生かせる特殊な状況の 1 つに、ループ文(for-to ループや for-in ループ)があります。
for var I: Integer := 1 to 10 do ...
for var Item: TItemType in Collection do...
型推論を利用することで、コードはさらにシンプルにすることができます:
for var I := 1 to 10 do ...
for var Item in Collection do ...
これは、以下のサンプル コードのように、限定されたスコープを持つ変数の場合、特に有効です。 ループの外で ‘I’変数を使用すると、コンパイラ エラーが発生します(これは今までは大抵の場合、警告のみでした)。
procedure ForTest;
begin
var total := 0;
for var I: Integer := 1 to 10 do
Inc (Total, I);
ShowMessage (total.ToString);
ShowMessage (I.ToString); // compiler error: Undeclared Identifier ‘I’