Help us understand the problem. What is going on with this article?

Delphi の型をレコードヘルパで拡張する

はじめに

Delphi には 2006 からクラスヘルパとレコードヘルパがあり、既存のクラスやレコード (構造体) を継承する事なく拡張する機能があります。あ、レコード (構造体) はそもそも継承できないんですけどね。

そして Delphi XE4 において普通の型ですらレコードヘルパで拡張できるようになりました。10.2 Tokyo 時点では以下のような組み込み型ヘルパがあります。

レコードヘルパ 説明
TBooleanHelper TBooleanHelper は、Boolean 型の機能を提供するレコード ヘルパです。
TByteBoolHelper TByteBoolHelper は、ByteBool 型の機能を提供するレコード ヘルパです。
TByteHelper TByteHelper は、Byte 型の機能を提供するレコード ヘルパです。
TCardinalHelper TCardinalHelper は、Cardinal 型の機能を提供するレコード ヘルパです。
TDoubleHelper 単精度浮動小数点値の低レベル操作の実行をサポートします。
TExtendedHelper 拡張精度(Extended 型)浮動小数点値に対する低レベル演算を実行できるようにします。
TGuidHelper TGUID のヘルパ型です。
TInt64Helper TInt64Helper は、Int64 型の機能を提供するレコード ヘルパです。
TIntegerHelper TNativeUIntHelper は、NativeUInt 型の機能を提供するレコード ヘルパです。
TLongBoolHelper TLongBoolHelper は、LongBool 型の機能を提供するレコード ヘルパです。
TNativeIntHelper TNativeIntHelper は、NativeInt 型の機能を提供するレコード ヘルパです。
TNativeUIntHelper TNativeUIntHelper は、NativeUInt 型の機能を提供するレコード ヘルパです。
TShortIntHelper TShortIntHelper は、ShortInt 型の機能を提供するレコード ヘルパです。
TSmallIntHelper TSmallIntHelper は、SmallInt 型の機能を提供するレコード ヘルパです。
TStringHelper インデックスが 1 から始まる文字列もインデックスが 0 から始まる文字列も含むすべての文字列扱うための関数やプロパティを提供するレコード ヘルパです。
TUInt64Helper TUInt64Helper は、UInt64 型の機能を提供するレコード ヘルパです。
TWordBoolHelper TWordBoolHelper は、WordBool 型の機能を提供するレコード ヘルパです。
TWordHelper TWordHelper は、Word 型の機能を提供するレコード ヘルパです。

例えば以前は IntToStr() を使って数値->文字列変換しなくてはなりませんでした。

var
  i: Integer;
  s: string;
begin
  i := 100;
  s := IntToStr(i);
end;

現在では以下のように書けます。

var
  i: Integer;
  s: string;
begin
  i := 100;
  s := i.ToString;
end;

コード補完が効くので便利ですね。

レコードヘルパを自作する

例えば TDateTime 型の実体は Double 型です。T が頭にありますがレコード (構造体) ではないため、Delphi XE4 より前ではヘルパが作れませんでしたが、今なら?

uDateTime.pas
unit uDateTime;

interface

uses
  System.SysUtils, System.DateUtils;

type
  TDayOfWeek = (Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday);

  TDayOfWeekHelper = record helper for TDayOfWeek
    function ToInteger: Integer;
  end;

  TDateTimeHelper = record helper for TDateTime
    function AddDays(Value: Word): TDateTime;
    function AddHours(Value: Word): TDateTime;
    function AddMilliseconds(Value: Word): TDateTime;
    function AddMinutes(Value: Word): TDateTime;
    function AddMonths(Value: Word): TDateTime;
    function AddSeconds(Value: Word): TDateTime;
    function AddYears(Value: Word): TDateTime;
    function Date: TDateTime;
    function Day: Word;
    function DayOfWeek: TDayOfWeek;
    function DayOfYear: Word;
    function DaysInMonth(const AYear, AMonth: Word): Word;
    function Hour: Word;
    function IsLeapYear(Year: Word): Boolean;
    function Millisecond: Word;
    function Minute: Word;
    function Month: Word;
    function Now: TDateTime;
    function Parse(const S: string): TDateTime; overload;
    function Parse(const S: string; const AFormatSettings: TFormatSettings): TDateTime; overload;
    function Second: Word;
    function Ticks: Int64;
    function TimeOfDay: TDateTime;
    function Today: TDateTime;
    function ToString(const Format: string = ''): string;
    function TryParse(const S: string; out Value: TDateTime): Boolean; overload;
    function TryParse(const S: string; out Value: TDateTime; const AFormatSettings: TFormatSettings): Boolean; overload;
    function Year: Word;
  end;

implementation

{ TDateTimeHelper }

function TDateTimeHelper.AddDays(Value: Word): TDateTime;
begin
  result := System.DateUtils.IncDay(Value);
end;

function TDateTimeHelper.AddHours(Value: Word): TDateTime;
begin
  result := System.DateUtils.IncHour(Value);
end;

function TDateTimeHelper.AddMilliseconds(Value: Word): TDateTime;
begin
  result := System.DateUtils.IncMilliSecond(Value);
end;

function TDateTimeHelper.AddMinutes(Value: Word): TDateTime;
begin
  result := System.DateUtils.IncMinute(Value);
end;

function TDateTimeHelper.AddMonths(Value: Word): TDateTime;
begin
  result := System.SysUtils.IncMonth(Value);
end;

function TDateTimeHelper.AddSeconds(Value: Word): TDateTime;
begin
  result := System.DateUtils.IncSecond(Value);
end;

function TDateTimeHelper.AddYears(Value: Word): TDateTime;
begin
  result := System.DateUtils.IncYear(Value);
end;

function TDateTimeHelper.Date: TDateTime;
begin
  result := System.DateUtils.DateOf(Self);
end;

function TDateTimeHelper.Day: Word;
begin
  result := System.DateUtils.DayOf(Self);
end;

function TDateTimeHelper.DayOfWeek: TDayOfWeek;
begin
  result := TDayOfWeek(System.SysUtils.DayOfWeek(Self) - 1);
end;

function TDateTimeHelper.DayOfYear: Word;
begin
  result := System.DateUtils.DayOfTheYear(Self);
end;

function TDateTimeHelper.DaysInMonth(const AYear, AMonth: Word): Word;
begin
  result := System.DateUtils.DaysInAMonth(AYear, AMonth);
end;

function TDateTimeHelper.Hour: Word;
begin
  result := System.DateUtils.HourOf(Self);
end;

function TDateTimeHelper.IsLeapYear(Year: Word): Boolean;
begin
  result := System.SysUtils.IsLeapYear(Year);
end;

function TDateTimeHelper.Millisecond: Word;
begin
  result := System.DateUtils.MilliSecondOf(Self);
end;

function TDateTimeHelper.Minute: Word;
begin
  result := System.DateUtils.MinuteOf(Self);
end;

function TDateTimeHelper.Month: Word;
begin
  result := System.DateUtils.MonthOf(Self);
end;

function TDateTimeHelper.Now: TDateTime;
begin
  result := System.SysUtils.Now;
end;

function TDateTimeHelper.Parse(const S: string; const AFormatSettings: TFormatSettings): TDateTime;
begin
  result := System.SysUtils.StrToDateTime(S, AFormatSettings);
end;

function TDateTimeHelper.Second: Word;
begin
  result := System.DateUtils.SecondOf(Self);
end;

function TDateTimeHelper.Parse(const S: string): TDateTime;
begin
  result := System.SysUtils.StrToDateTime(S);
end;

function TDateTimeHelper.Ticks: Int64;
begin
  result := System.DateUtils.MilliSecondOf(Self) * 10000;
end;

function TDateTimeHelper.TimeOfDay: TDateTime;
begin
  result := System.DateUtils.TimeOf(Self);
end;

function TDateTimeHelper.Today: TDateTime;
begin
  result := System.DateUtils.Today;
end;

function TDateTimeHelper.ToString(const Format: string): string;
begin
  if Format = '' then
    result := System.SysUtils.DateTimeToStr(Self)
  else
    result := System.SysUtils.FormatDateTime(Format, Self)
end;

function TDateTimeHelper.TryParse(const S: string; out Value: TDateTime;
  const AFormatSettings: TFormatSettings): Boolean;
begin
  result := System.SysUtils.TryStrToDateTime(S, Value, AFormatSettings);
end;

function TDateTimeHelper.TryParse(const S: string; out Value: TDateTime): Boolean;
begin
  result := System.SysUtils.TryStrToDateTime(S, Value);
end;

function TDateTimeHelper.Year: Word;
begin
  result := System.DateUtils.YearOf(Self);
end;

{ TDayOfWeekHelper }

function TDayOfWeekHelper.ToInteger: Integer;
begin
  result := Ord(Self);
end;

end.

.NET の DateTime 構造体パクったインスパイアしたレコードヘルパも簡単に作れます。

使い方は uses 句にこのユニットを追加するだけです。

uses
  ..., uDateTime;

おわりに

この XE4 から拡張されたレコードヘルパは列挙型等に対しても使えます。上の uDateTime.pas では TDayOfWeek 列挙型に対するレコードヘルパ TDayOfWeekHelper がありますね。

var
  s: string;
begin
  s := Now.DayOfWeek.ToInteger.ToString;
end;

このコードは今日の曜日を表す TDayOfWeek を数値に変換したものを文字列に変換しています。今日が日曜日の場合、S には '0' が入ります (6=土曜日)。

従来からのレコードヘルパーも含め、この機能は Fortran 2003Oberon-2 でいう所の Type-Bound Procedures に近い考え方だと思います。

See also:

ht_deko
とある熊本の障害復旧(トラブルシューター)
https://ht-deko.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした