2012年5月8日

列挙型と列挙子名(文字列)の相互変換(ジェネリックス版)

ずいぶん前に列挙型の値と列挙子の名前を相互変換する方法について取り上げましたが、これを関数化しようとすると結局のところ必要な列挙型すべてについて個別に実装の必要がありました。ところがLynaたんさん(talesさん)ジェネリクスを使って少しだけ手軽に列挙型の値を文字列に変換する。 - 全力わはーではDelphi 2009の新機能のジェネリックスを利用することでこれを単一の実装で実現しています。ということでLynaたんさんが提示した手法で列挙型と列挙子名の相互変換を関数化してみました(単なるパクリですいません)。ここではクラスではなく高度なレコード型を使用します。またジェネリックスを使いますので当然のことながらDelphi 2009以降でのみ有効です。
uses
  TypInfo, SysUtils, SysConst;

type
  TEnumHelper = record
    class function GetEnumName<T>(Value: T): String; static;
    class function GetEnumValue<T>(const Name: String): T; static;
  end;

class function TEnumHelper.GetEnumName<T>(Value: T): String;
var
  P: PTypeInfo;
  IValue: Integer;
begin

  P := TypeInfo(T);
  if P = nil then
  begin
    raise EInvalidOpException.CreateRes(@SVarNotImplemented);
  end;

  IValue := 0;
  Move(Value,IValue,SizeOf(T));
  Result := TypInfo.GetEnumName(P,IValue);

end;

class function TEnumHelper.GetEnumValue<T>(const Name: String): T;
var
  P: PTypeInfo;
  IValue: Integer;
begin

  P := TypeInfo(T);
  if P = nil then
  begin
    raise EInvalidOpException.CreateRes(@SVarNotImplemented);
  end;

  IValue := TypInfo.GetEnumValue(P,Name);

  with GetTypeData(P)^ do
  begin
    if (IValue < MinValue) or (IValue > MaxValue) then
    begin
      raise ERangeError.CreateRes(@SRangeError);
    end;

    Result := Default(T);
    Move(IValue,Result,SizeOf(T));
  end;

end;

TEnumHelper.GetEnumNameの型パラメータは省略可能です(引数の型から自動的に推論されます)が、TEnumHelper.GetEnumValueについては型パラメータが必須となります。
var
  Alignment: TAlignment;
begin

  Alignment := TEnumHelper.GetEnumValue<TAlignment>('taLeftJustify');

end;

var
  S: String;
begin

  S := TEnumHelper.GetEnumName(taLeftJustify);

end;

こんな感じで使うことができます。

0 件のコメント: