LoginSignup
2

More than 1 year has passed since last update.

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

Last updated at Posted at 2018-04-14

はじめに

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 に近い考え方だと思います。

Delphi 11.0 Alexandria で、TDatetime 型に対するレコードヘルパ (System.DateUtils.TDateTimeHelper) が追加されました。

See also:

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2