Caché の今回のリリースでは、Caché オブジェクト・テクノロジーに関する複数の重要な機能が強化されています。これらの機能や、既存のオブジェクトに対する影響について、以下のセクションで説明しています。
互換性とアップグレード
Caché オブジェクトの今回のリリースは、旧リリースとのソース互換を持つように設計されています。しかし、クラス・コンパイラやクラス・ディクショナリ、オブジェクト・コード形式には、内部的に変更されている部分が多くあります。このため、アップグレードには、すべてのクラスのリコンパイル、およびすべてのクラス定義の内部的な変換も必要です。
Note:
v5.0 開発者キットの旧バージョンからアップグレードする場合は、ここで指定されているアップグレードの手順に従わなければなりません。
クラス定義のアップグレード
Caché の今回のバーションでは、新しい、よりコンパクトなクラス・ディクショナリ (Caché 内のクラス定義を保持するのに使用される構造) を導入しています。Caché は、クラス・ディクショナリを新しい構造に自動的にアップグレードするメソッドを提供します。
また、既存のクラス定義を外部ファイル (CDL ファイル等) にエクスポートし、新規の Caché をインストールした後にそのファイルをリロードすることもできます。次の章の クラス定義の移行 を参照してください。
既存の Caché システムをアップグレードする場合、以下のように Caché コマンド行で Upgrade() コマンドを実行して、ネームスペース内のすべてのクラス定義をアップグレードすることができます。
 Do $system.OBJ.Upgrade()
このコマンドを使用して、現在のネームスペース内のすべてのクラスに対する、クラス・ディクショナリをアップグレードできます。アップグレードされたクラスは、使用前にリコンパイルする必要があります。
以下のように Caché コマンド行で CompileAll() コマンドを実行することで、ネームスペース内のすべてのクラスをリコンパイルできます。
 Do $system.OBJ.CompileAll()
すべてのネームスペース内のクラス・ディクショナリをアップグレードする新規のコマンドも追加されています。
 Do $system.OBJ.UpgradeAll()
リコンパイルとアップグレードを同時に行いたい場合は、オプションの “c” (コンパイル) フラグを Upgrade メソッドと UpgradeAll メソッドの両方に渡します。
 Do $system.OBJ.Upgrade("c")
また、すべてのネームスペースのすべてのクラスを更新する ^%UPDATECLASSES ルーチンを実行することもできます。
 Do ^%UPDATECLASSES
^%UPDATECLASSES ルーチンを使用すると、各ネームスペースのクラスをターミナルに 1 つずつ更新したり、バックグラウンドで複数のプロセスを実行することにより、すべてのネームスペースを一括で更新することができます。
$system.OBJ.Upgrade() コマンドは、ディスク上に格納されている現在のクラス定義のアップグレードを実行します。これは、クラス定義にのみ影響を与え、ユーザ・データには影響を与えません。“ダウングレード” に対応する関数はありません。ダウングレードの能力を保持したい場合は、アップグレード前にクラス定義を外部ファイル (例えば、バージョン 4.x を使用している場合は CDL ファイル) にエクスポートしておきます。ダウングレードするには、保存しておいたクラス定義をリロードします。
クラス定義の移行
Caché の旧バージョンのオブジェクト・アプリケーションを、新バージョンに移行する場合は、以下の手順に従います。
  1. Caché の旧バージョンから、既存のクラス定義を 1 つ、または複数の CDL ファイルにエクスポートします。
  2. CDL ファイル (1 つまたは複数) を新バージョンの Caché にロードします。以上の手順は、Caché スタジオや Caché エクスプローラを使用するか、または Caché コマンド行から実行することもできます。
     Do $system.OBJ.Load("myfile.cdl","c")
  3. クラスをコンパイルします。
    Important:
    コンパイラ・エラーを避けるために、コンパイルを実行する前に、[プロセスごとの最大メモリ (KB)] の設定を最高値 (16384 KB) に更新します。この設定は、Caché 構成マネージャ[詳細] タブの [プロセス] にあります。旧バージョンの Caché における既定の設定は 256 KB でした。
互換性
基本的には旧バージョンとの互換性を確保しておりますが、一部 Caché オブジェクトの旧バージョンとの互換性がない場合もあります。以下はその例です。
オブジェクト開発機能
今回のバージョンでは、オブジェクト・ベースのアプリケーションの開発に関連する、多くの新機能や強化された機能が提供されています。
Caché スタジオ
新バージョンの Caché スタジオは、ルーチンの編集だけでなく、クラスの編集もサポートしています。Caché スタジオの新機能は、以下の通りです。
クラスの編集やデバッグをサポートするために、新規のスタジオは Caché v5.0 以降で利用可能なサーバ機能を必要とします。 したがって、Caché の古いバージョンで、このバージョンのスタジオを使用することはできません。
詳細は、スタジオの機能の完全な概要を掲載している "Caché スタジオの使用法" を参照してください。ナレッジ・ベースの スタジオに関するよくある質問 も利用できます。
Caché オブジェクト・アーキテクト
Caché オブジェクト・アーキテクトは、今回のリリースでは Caché から外され、そのすべての機能は、Caché スタジオに統合されました。
CDL ファイルと XML ファイル
XML サポートの向上のために、ファイル内のクラス定義の表示方法として、Caché は新しい XML ベースの形式をサポートしています。この形式は、従来の形式にはない、以下のような複数の利点を提供します。
現在、スタジオのインポート、またはエクスポートのすべての機能は、この形式を使用します。
プロジェクト定義、ルーチン、グローバル、および CSP ファイルを、この XML ファイル内に格納することもできます。
従来の CDL (クラス定義言語) 形式は、今回のリリースでもサポートされており、Caché に CDL 形式をインポートし、Caché からエクスポートすることができます。今回のリリースを開始するときは、新しい XML 形式を使用しなければなりません。
インポート・ユーティリティは、ファイル拡張子を使用して、クラス定義をロードするのに、従来の CDL 形式 (.cdl) または新規の XML ベースの形式 (.xml) のどちらを使用するかを決定します。
バージョン 4.x のクラス定義のエクスポート
今回のバージョン 5 のオブジェクト・モデルでは、前回のバージョン 4 にはない多くの新機能が提供されています。 バージョン 5 の CDL ファイルをエクスポートする場合、そのファイルはバージョン 4 システムにはロードすることができません (既定)。
バージョン 5 で定義されているクラスをバージョン 4 で使用する必要がある場合は、Caché コマンド行で $System.OBJ.ExportCDL メソッド (%SYSTEM.OBJ クラスを参照) を使用し、フラグのリストで “4” を指定します。“4” フラグが存在する場合、CDL はバージョン 4.x のシステムとの互換性がない構文を含みません。
Note:
ExportCDL は、互換性のない機能 (PROCEDUREBLOCK/WEBMETHOD など) がエクスポートされたクラスに存在する場合、エクスポートされた CDL ファイルにコメントを追加します。 ExportCDL は、4.x システムに存在しないスーパー・クラスやタイプ、および LANGUAGE 値をチェックしません。
テキスト・ベースのクラス定義
Caché スタジオでは、ドキュメントの例にあるように、新規のテキスト・ベースのクラス定義が使用されます。この新規のクラス定義は、CDL よりも容易に使用でき、スタジオで直接編集できるように設計されています。
構文に関する詳細は、Caché スタジオの使用法クラス定義言語 の章を参照してください。
クラス・コンパイラの強化された機能
Caché クラス・コンパイラは、以下のように機能拡張されています。
Note:
これらの強化された機能は、以下を除いて、アプリケーションのパフォーマンスを向上させる以外に影響はありません。使用しているアプリケーションが、^oddDEF グローバルもしくは ^oddCOM グローバルへの直接参照を使用して、クラス・ディクショナリ構造への直接参照を作成する場合、またはそのアプリケーションがクラス・ディクショナリ構造を参照するマクロを使用する場合、この情報にアクセスするには、ドキュメント化されたマクロを使用するようにアプリケーションを変更する必要があります。
オブジェクト・モデルの強化された機能
Caché オブジェクト・モデルには、強化された多くの機能が提供されています。以下は その概要です。
これらすべては、以前のバージョンと上位互換性があります。
新規のクラス定義クラス
今回のリリースでは、Caché クラス・ディクショナリへのオブジェクト・アクセスを提供する、一連の新しいクラス定義クラス (%Dictionary.ClassDefinition など) があります。従来のクラス定義クラスも、互換性のために使用できます。詳細は、クラス定義クラス を参照してください。
プロシージャ・ブロック・メソッド
クラスやメソッド・レベルで ProcedureBlock キーワードを指定することにより、Caché ObjectScript 内でプロシージャ・ブロックのサポートを使用できるようになります。
プロシージャ・ブロックは、メソッドの変数スコープのルールをより自然な振る舞いに変更します。
作成された新規の任意のクラスは、プロシージャ・ブロックを使用します (使用できないようにすることもできます)。既存のクラスには、変更はありません。今回の変更は、既存のコードが今回のリリースで互換性を持つように設計されています。
オブジェクト・ベースのメソッド・ジェネレータ
メソッド・ジェネレータは、オブジェクトを利用して (メタ情報を提供し、コード生成を制御するため)、開発が非常に容易になりました。従来のメソッド・ジェネレータも、互換性のために使用できます。詳細は、メソッド・ジェネレータ を参照してください。
%ResultSet の新しいインタフェース
%ResultSet クラスは、多次元プロパティ Data を使用した、結果セットの現在の行の値へのアクセスを提供します。
%ResultSet オブジェクトの一般的な使用は、各フィールド値を取得するための、多くのメソッドの呼び出しを使用するコードを導きます。例えば、以下は一般的なループです。
   While (result.Next()) {
      Write result.Get("Name")," - ",result.Get("Address"),!
   } 
Get の各呼び出しは、プロパティ値の読み込みよりも高価なメソッドの呼び出しです。結果セットへのアクセスをより高速にするため、新しい多次元プロパティ Data を使用することができます。このプロパティの値は、列名でインデックスされるので、上記のループは以下のようになります。
   While (result.Next()) {
      Write result.Data("Name")," - ",result.Data("Address"),!
   } 
%ResultSet クラスは今回のリリースでも Get メソッドをサポートし、後方互換性を持ちます。新規のコードでは、Data 多次元プロパティも使用できます。
SQL ストアド・プロシージャのより単純な定義
ストアド・プロシージャとして投影されたメソッドは、最初の仮引数をタイプ %SQLProcContext として宣言する必要はありません。新しい変数 %sqlcontext を、仮引数の代わりに使用することができます。メソッドが %SQLProcContext のタイプとして宣言された最初の引数を持っている場合、ストアド・プロシージャとしてサーバからメソッドが呼び出されたとき、%sqlcontext が最初の引数として渡されます (既存のメソッドとの互換性を確実にするため)。引数の有無を確認するための要件は、以前と同様です。
例えば、
ClassMethod UpdateProcTest(zip As %String, city As %String, state As %String)
             As %Integer [ SqlProc ] {
   New SQLCODE,%ROWCOUNT,%ROWID,rowcount
   &sql(UPDATE Sample.Person
         SET Home_City = :city, Home_State = :state WHERE Home_Zip = :zip)

  // Return context information to client via %SQLProcContext object
  If ($g(%sqlcontext)'=$$$NULLOREF) {
      Set %sqlcontext.SQLCode = SQLCODE
         Set %sqlcontext.RowCount = %ROWCOUNT
   }
  QUIT 1
}
この変更は完全に、後方互換性のためです。 既存のストアド・プロシージャは変更しないで実行できます。
ユーザ定義のクラス・クエリ・メソッドの配列としての QHandle
クラス・クエリのさまざまなメソッドに、配列として渡される QHandle 引数を使用することができます。つまり、QHandle 変数のサブノード内にデータを保存できるということです。この値は、QHandle がさまざまなクエリ・メソッドに渡される際に必ず存在します。この変更は、オブジェクト参照のシステム・サポートとの互換性を持つ、QHandle を使用してオブジェクトを容易に渡すために設計されています。
既存のコードは実行を継続します。 この場合、QHandle は、トップ・レベルのノードのみが定義されている配列とみなすことができます。
XML のサポート
今回のリリースでは、Caché は一連の XML ベースの豊富な機能をサポートします。その機能は、以下の通りです。
XML サポートに関する詳細は、Caché での XML の使用法 を参照してください。
Web サービス、および SOAP に関する詳細は、Caché での SOAP と Web サービスの使用法 を参照してください。
Basic と Java メソッドのサポート
Caché の今回のリリースでは、Caché ObjectScript に加え、Basic や Java のメソッドを定義することもできます。
Class MyClass Extends %RegisteredObject {

/// A Basic Method
Method MyMethod() As %Integer [language = basic]
{
   For i = 1 To 10
      person = New Sample.Person()
      person.Name = "John " & i
      person.%Save()
   Next

   Return 1
}

/// A Java method
Method Add(a As %Integer, b As %Integer) As %Integer [language = java]
{
   return a + b;
}
}
Basic メソッドは実行可能な Caché コードにコンパイルされ、Caché 仮想マシンによって (Caché ObjectScript とまったく同じ方法で) 実行されます。Java メソッドは Caché Java バインディングによって生成された Java クラスに自動的に配置され、Java 仮想マシン (JVM) で実行されます。Caché は JVM を提供しませんが、標準 JVM で使用できます。
詳細は、リリース・ノートCaché 言語の新機能 のセクションを参照してください。
Java サポートに関する詳細は、Caché での Java の使用法 を参照してください。
Caché アクティベート ActiveX ゲートウェイ
Caché の今回のリリースでは Caché アクティベートを提供しています。アクティベートは ActiveX ゲートウェイであり、アプリケーションが、Caché からの ActiveX コンポーネントをネイティブ・オブジェクトであるかのように使用できる機能を提供します。また、スタジオからもアクセス可能なウィザードでは、ActiveX コンポーネントから ActiveX ラッパ・クラスを自動的に生成することもできます。
詳細は、Caché アクティベート を参照してください。
オブジェクト参照のシステム・サポート
Caché の今回のリリースでは、Caché の実行時エンジン管理オブジェクト参照を持つことで、オブジェクトの能力を向上させています。以前のバージョンでは、オブジェクト参照 (OREF) の管理は、オブジェクト・ライブラリによって処理されます。 基本のシステムは OREF の値とその他の値を区別しません。システムの OREF のサポートによって、実行時のエンジンは特定の値を OREF として認識し、オブジェクトのライフ・サイクルの管理と参照カウントを自動的に提供します。
特に、以下のことを意味します。
参照カウント
オブジェクト変数は、参照カウントを自動的に管理します。 参照カウントとは、現在オブジェクトを参照している項目の数です。オブジェクトを参照する変数やオブジェクトのプロパティを設定する度に、オブジェクトの参照カウントは自動的にインクリメントされます。変数がオブジェクトへの参照を停止 (範囲外になった場合、削除された場合、または新しい値が設定された場合) すると、参照カウントはディクリメントされます。このカウントが 0 になると、オブジェクトは自動的に消滅します (メモリから削除されます)。
例えば、以下のメソッドを考えてみます。
Method Test()
{
  Set person = ##class(Sample.Person).%OpenId(1)

  Set person = ##class(Sample.Person).%OpenId(2)
}
このメソッドは Sample.Person のインスタンスを作成し、そのインスタンスに対する参照を変数 person に配置します。そして、Sample.Person の別のインスタンスを作成し、person の値を新しいインスタンスに対する参照に変更します。ここで、最初のオブジェクトを参照しているものがなくなり、消滅します。メソッドの最後で、person は範囲外になり (このメソッドがプロシージャ・ブロックを使用するので)、2 番目のオブジェクトは消滅します。以前のバージョンでは、これらのオブジェクトはクローズされていませんでした (%Close メソッドに対する明示的な呼び出しがない場合)。
他の例は、コレクション内のオブジェクトを繰り返します。
Method Total(invoice As Invoice) As %Integer
{
   Set total = 0
   Set count = invoice.LineItems.Count()

   For i = 1:1:count {
      Set lineitem = invoice.LineItems.GetAt(i)
      Set total = total + lineitem.Amount
   }

   Quit total
}
ここでは、LineItems コレクション内の各オブジェクトを連続してオープンし、その Amount を実行中の合計に追加します。オブジェクトが変数 lineitem に割り当てられるたび、それが参照していた前のオブジェクトはメモリから削除されます。
任意のローカル変数、またはローカル配列ノードは、OREF 値を含むこともあります。以下はその例です。
 Set array(1) = ##class(Sample.Person).%New()
 Set array(2) = ##class(Sample.Person).%New()
 Set array(3) = ##class(Sample.Person).%New()
 Set array(4) = "string"
 Kill array
ここでは、3 つの新規のオブジェクト・インスタンスを生成し、それに対する参照を array の 3 つのノードに配置します。配列を削除するとき、オブジェクトは消滅します (それに対する参照がないからです)。上記の array(4) のように、配列内ではオブジェクト値と非オブジェクト値を混合できます。
OREF 値を非オブジェクト値として使用する
OREF 値を文字列として使用する場合、値は文字列に自動的に変換されます。
 Set object = ##class(Sample.Person).%New()
 Write object      // implicitly convert object to a string
この例は object を、文字列 “6@Sample.Person” (OREF の数値と “@” 文字、およびそのクラス名) に暗黙的に変換します。
OREF は、以下の場合に文字列へ変換されます。
OREF 値を整数として使用する場合、OREF 値は数字に変換されます。
 Set object = ##class(Sample.Person).%New()
 Write +object      // implicitly convert object to a number
Note:
文字列や数値は、オブジェクト参照に変換できません。
文字列や数値を OREF として使用しようとすると、<INVALID OBJECT> エラーが発生します。以下はその例です。
 Set object = ##class(Sample.Person).%New()
 Set object.Name = "Jack"

 Set x = +object      // set x to numeric value of OREF
 Write x.Name         // <INVALID OBJECT>
これは、値が一度、非 OREF に変換されると、それに関連する参照カウントが存在しなくなるからです。文字列や数値を OREF に変換する能力は、信頼できないコードを生成する可能性があります。 その変換を行うと、同じオブジェクトが存在し続けることを保証できません。
%New メソッド
%New メソッドは、新しいシステム・レベルの最適化機能を備え、オブジェクトの作成がより高速になりました。
%Close メソッド
システム OREF のサポートにより、%Close メソッドの役目はなくなりましたが、既存のアプリケーションとの互換性のために存在します。
%Close の従来の振る舞い
今回のリリースへのアップグレードのプロセスを簡素化するため、%Close メソッドの従来の振る舞いを有効にする方法があります。既定では、%Close はバージョン 5 では何も行いません。
現在のプロセスに対する %Close の従来の振る舞いを有効にする方法は以下の通りです。
 Do $ZU(68,56,1)
現在のプロセスに対する %Close の従来の振る舞いを無効にする方法は以下の通りです。
 Do $ZU(68,56,0)
また、すべてのプロセスに対して %Close の従来の振る舞いを設定することもできます。
 Do $ZU(69,56,1)
これは、このコマンドの 呼び出し後 に開始したすべてのプロセスに影響を与えます。 このコードは、使用しているシステムの初期化ルーチンに配置します。
%Close の従来のサポートを有効にすると、%Close メソッドは旧バージョンと同様に動作を継続します。これはオブジェクトのオブジェクト・レベル参照カウントをディクリメントし、そのカウントが 0 に到達すると、メモリから削除します。 従来のモードでは、オブジェクトはシステム・レベルのカウントに加え、オブジェクト・レベルの参照カウントも含むことに注意してください。このオブジェクト・レベルのカウントは、システムの OREF 管理に影響を受けません。
システムの OREF サポートによって、以下のコードはオブジェクト・インスタンスを作成し、そのインスタンスに対するすべての参照が削除されたときに、そのインスタンスを削除します。
 Set obj = ##class(Sample.Person).%New()
 Set obj = "" // object is now destroyed
アプリケーションがあるオブジェクトの %Close を呼び出した場合、そのオブジェクトはその時点 (参照カウントが 0 になったとみなされて) 削除され、使用されていた OREF 値は “不活性化”されます。つまり、この OREF 値に対するすべての参照が削除されるまで、その値は他のオブジェクトを参照するのに使用されないということです。
 Set obj = ##class(Sample.Person).%New()
 Do obj.%Close()   // object is closed; OREF value is "suspended"
 Set obj = ""      // OREF value is removed
停止中の OREF 値を使用すると、実行時エラーが発生します。これは以前のバージョンでも見られた、オブジェクトがクローズされた後に、そのオブジェクトを使用することと、基本的に同じ振る舞いです。
Note:
%Close メソッドの従来のサポートは、Caché バージョン 5 以降では サポートされない 予定です。その時点で、%Close メソッドの機能はまったく消滅します (そしてすべてのアプリケーションの動作速度がわずかに向上します)。v5.0 にアップグレード後、アプリケーションが %Close の新しい振る舞いの元で正常に作動するかを確認するテストを行う必要があります。 このテストを行うことで、将来のバージョンへのアップグレードが可能になります。%Close に依存してオブジェクトをクローズすることは不可能であることに注意してください (オブジェクトに対する未決の参照が存在することを伝えていないため)。このため、アプリケーションの大半は、変更しないで新しい %Close の振る舞いを使用して実行することができます。
$IsObject 関数
今回のバージョンには、任意の変数や式が、有効なオブジェクト参照を持っているかどうかをテストする、$IsObject という新しい関数があります。以下の 3 つの値のうち、いずれか 1 つを返します。
1 変数は、オブジェクト参照を持っています。
0 変数は、オブジェクト参照を持っていません。
-1 変数は、%Close メソッドに対する呼び出しの結果として “無効” になったオブジェクトに対する参照を持ちます。これは、%Close の従来の振る舞い を使用可能にしている場合のみ発生します。
一連のプロパティ参照の NULL 値
今回のリリースでは、埋め込みオブジェクト参照が、その参照のプロパティを取得する前に設定されているかどうかの確認を不要にするため、Caché オブジェクトの機能を強化しています。多くの場合、オブジェクト参照が設定されていないとき、プロパティ値は空文字列として処理されます。今回のリリースでは、Object.Embedded が既存のオブジェクト参照に対して設定されていない場合、Object.Embedded.Property 形式のプロパティ参照は空文字列を返します。これは以前のバージョンでは、<INVALID OREF> エラーの原因となっていました。
Caché ObjectScript では、以下のコードは、
 Write Object.Embedded.Property
以下と同等です。
 Write $SELECT(Object.Embedded'=$$$NULLOREF:Object.Embedded.Property,1:"")
システム・オブジェクト参照のサポートを使用するためのアップグレード
システム OREF サポートの大半は、現在のシステムとの上位互換性を持っています。OREF 値を非オブジェクト (整数、または文字列) で使用するアプリケーションでは、問題が生じる場合があります。これは、以下のセクションで詳細に説明します。
グローバルの OREF
OREF がグローバル内に保存されるとき、OREF は文字列値に変換されます。以下のコードは、旧バージョンでは動作していましたが、Caché 5.0 では実行時エラーの原因となります。 文字列値をオブジェクト参照として使用しているからです。
 Set obj = ##class(Sample.Person).%New()
 Set ^global(1) = obj
 //...
 Set a = ^global(1)
 Write a.Name   // <INVALID OBJECT> error
非 OREF 値を OREF 値として使用する
数値は OREF として使用できないので、以前のバージョンで使用していた (1 の値を持つ OREF が存在する場合) 以下のコードを使用すると、実行時エラーが発生します。
 Do (1).Method()  // <INVALID OBJECT> error
文字列と $Lists で OREF を使用する
OREF 値を文字列の一部として、または $List 構造の内部 (システムに関する限り、$List 構造は文字列) に保存するとき、OREF は文字列値に変換されます。以前のバージョンで使用していた以下のコードを使用すると、OREF 値として文字列を使用しようとするため、実行時エラーが発生します。
 Set obj = ##class(Sample.Person).%New()
 Set list = $ListBuild(obj)
 // ...
 Set x = $ListGet(list,1)
 Write x.Name   // <INVALID OBJECT> error
区切り文字列、または $List 構造内部に OREF 値を格納する場合、使用中のコードを変更する必要があります。この場合、そのコードを変換してローカル配列を (OREF 値を含むことができる) 使用することをお勧めします。
 Set obj = ##class(Sample.Person).%New()
 Set array(1) = obj
Caché 外部で OREF を使用する
特定のアプリケーションは、Caché プロセスと外部コンテキストの間に OREF 値を渡します。例えば、OREF の整数値をクライアント・アプリケーション (Java や Visual Basic など) に送り、その後、オブジェクト参照として使用を継続しながら、その値をサーバに返します。整数値を OREF に戻す変換はできないので、この操作はできなくなりました。
Note:
ドキュメント化された形式で Caché で提供されている標準 Active X と Java バインディングを使用している場合は、クライアントとサーバ間でオブジェクトを使用しても問題はありません。 Caché が自動的にこの状況を管理します。
Caché 外部でオブジェクトを渡し、それを戻した場合、以下の技術を使用することをお勧めします。
  1. クライアントに送信したオブジェクト参照の追跡するために、サーバ上でローカル配列を使用します。
  2. この配列内で、添え字として (他の OREF 値との衝突を避けるため) OREF の数値を使用し、ノード値として OREF 値を配置して、OREF を保存します。
     Set %myobjects(+obj) = obj
    この配列に保存されたオブジェクト参照は、ユーザがオブジェクトを使用する準備ができるまで、そのオブジェクトは存在することを保証し、真の OREF を提供します。