10

TypeInfo(Type) は指定された型に関する情報を返します。var の typeinfo を知る方法はありますか?

var
  S: string;
  Instance: IObjectType;
  Obj: TDBGrid;
  Info: PTypeInfo;
begin
  Info:= TypeInfo(S);
  Info:= TypeInfo(Instance);
  Info:= TypeInfo(Obj);
end

このコードは次を返します。

[DCC エラー] Unit1.pas(354): E2133 TYPEINFO 標準関数には型識別子が必要です

インスタンス化されていない var は単なるポインター アドレスであることはわかっています。コンパイル時に、コンパイラは解析し、型の安全性チェックを行います。

実行時に、アドレスを渡すだけで var についてもう少し知る方法はありますか?

4

3 に答える 3

31

いいえ。

まず、「インスタンス化されていない変数」のようなものはありません。名前を入力してソースファイルに入力するだけでインスタンス化できます。

次に、ソースコードで変数を確認することで、変数について知っておくべきことがすべてわかっています。プログラムがコンパイルされると、変数は存在しなくなります。その後、それはすべてほんの少しです。

ポインタはコンパイル時にのみ型を持ちます。実行時に、そのアドレスに対して実行できることはすべてすでに決定されています。すでに述べたように、コンパイラはそれをチェックします。実行時に変数の型をチェックすることは、動的言語のように、変数の型が変更される可能性のある言語でのみ役立ちます。それに最も近いDelphiは、そのVariantタイプです。変数のタイプは常にVariantですが、多くのタイプの値を格納できます。それが何を保持しているかを知るために、VarType関数を使用することができます。

TypeInfo変数に関連付けられた型の型情報を取得するために使用したい場合はいつでも、関心のある型に直接名前を付けることができます。変数がスコープ内にある場合は、その宣言を見つけて、への呼び出しで宣言された型を使用できますTypeInfo

関数に任意のアドレスを渡し、その関数にそれ自体の型情報を検出させたい場合は、運が悪いです。PTypeInfo代わりに、値を追加のパラメーターとして渡す必要があります。これが、すべての組み込みDelphi関数が行うことです。たとえばNew、ポインタ変数を呼び出すと、コンパイラは、PTypeInfo割り当てている型の値を保持する追加のパラメータを挿入します。動的配列を呼び出すとSetLength、コンパイラーPTypeInfoは配列型の値を挿入します。

あなたが与えた答えは、あなたが求めていたもの以外のものを探していることを示唆しています。あなたの質問を考えると、私はあなたがこのコードを満たすことができる架空の関数を探していると思いました:

var
  S: string;
  Instance: IObjectType;
  Obj: TDBGrid;
  Info: PTypeInfo;
begin
  Info:= GetVariableTypeInfo(@S);
  Assert(Info = TypeInfo(string));

  Info:= GetVariableTypeInfo(@Instance);
  Assert(Info = TypeInfo(IObjectType));

  Info:= GetVariableTypeInfo(@Obj);
  Assert(Info = TypeInfo(TDBGrid));
end;

JCLのIsClassandIsObject関数を使用して、その関数を作成してみましょう。

function GetVariableTypeInfo(pvar: Pointer): PTypeInfo;
begin
  if not Assigned(pvar) then
    Result := nil
  else if IsClass(PPointer(pvar)^) then
    Result := PClass(pvar).ClassInfo
  else if IsObject(PPointer(pvar)^) then
    Result := PObject(pvar).ClassInfo
  else
    raise EUnknownResult.Create;
end;

明らかにそれ以上では機能しませんSInstance、どうなるか見てみましょうObj

Info := GetVariableTypeInfo(@Obj);

これにより、アクセス違反が発生するはずです。Objには値がないためIsClassIsObjectどちらも指定されていないメモリアドレスを読み取ります。おそらく、プロセスに属するアドレスではありません。変数のアドレスを入力として使用するルーチンを要求しましたが、単なるアドレスでは不十分です。

IsClassそれでは、IsObject実際にどのように動作するかを詳しく見てみましょう。これらの関数は任意の値を取り、その値がオブジェクト(インスタンス)またはクラスのいずれかの特定の種類の値である可能性があるかどうかを確認します。次のように使用します。

// This code will yield no assertion failures.
var
  p: Pointer;
  o: TObject;
  a: array of Integer;
begin
  p := TDBGrid;
  Assert(IsClass(p));

  p := TForm.Create(nil);
  Assert(IsObject(p));

  // So far, so good. Works just as expected.
  // Now things get interesting:

  Pointer(a) := p;
  Assert(IsObject(a));
  Pointer(a) := nil;
  // A dynamic array is an object? Hmm.

  o := nil;
  try
    IsObject(o);
    Assert(False);
  except
    on e: TObject do
      Assert(e is EAccessViolation);
  end;
  // The variable is clearly a TObject, but since it
  // doesn't hold a reference to an object, IsObject
  // can't check whether its class field looks like
  // a valid class reference.
end;

関数は変数について何も教えてくれず、保持しているについてだけ教えてくれることに注意してください。それでは、変数に関する型情報を取得する方法についての質問に答えるために、これらの関数についてはあまり考慮しません。

さらに、変数について知っているのはそのアドレスだけだとあなたは言いました。あなたが見つけた関数は変数のアドレスを取りません。それらは変数の値を取ります。これがデモンストレーションです:

var
  c: TClass;
begin
  c := TDBGrid;
  Assert(IsClass(c));
  Assert(not IsClass(@c)); // Address of variable
  Assert(IsObject(@c)); // Address of variable is an object?
end;

明らかにゴミであるものをそれらに渡すことによって、私がこれらの関数を悪用している方法に反対するかもしれません。しかし、それがこのトピックについて話すことが理にかなっている唯一の方法だと思います。ガベージ値がないことがわかっている場合は、変数に実際の型を使用するためのプログラムについてすでに十分に知っているので、とにかく要求している関数は必要ありません。

全体的に、あなたは間違った質問をしている。変数のタイプやメモリ内の値のタイプをどのように決定するかを尋ねる代わりに、変数とデータのタイプをまだ知らない位置に自分自身をどのように導いたかを尋ねる必要があります

于 2009-02-16T18:54:47.990 に答える
2

私が知っていることではありません。クラスの公開されたプロパティの RTTI (Run Time Type Information) を取得できますが、文字列や整数などの「通常の」変数については取得できません。情報は単にそこにありません。

さらに、型を渡さずに var を渡す唯一の方法は、ジェネリック TObject パラメータ、ジェネリック型 (D2008、のように)、または型なしパラメータとして使用することです。コンパイルさえできる別の渡し方は考えられません。

于 2009-02-16T18:38:55.593 に答える