プロパティ・メソッドの使用とオーバーライド
ここでは、プロパティ・メソッドについて説明します。OREF を使用してオブジェクトのプロパティを操作するとき、プロパティ・メソッドは InterSystems IRIS® データ・プラットフォームによって使用される実際のメソッドです。これらのメソッドは、SQL 経由でデータにアクセスするときは使用されません。
プロパティ・メソッドの概要
プロパティには、自動的に関連付けられた多くのメソッドがあります。これらのメソッドは、標準の継承を介して継承されません。その代わりに、各プロパティに対する一連のメソッドを作成するために、特別なプロパティの振る舞いメカニズムを使用します。
各プロパティは、2 つの場所からの一連のメソッドを継承します。
-
Get()、Set()、および確認コードなど、組み込みの動作を提供する %Property クラス。
-
プロパティによって使用されるデータ型クラス (該当する場合)。これらのメソッドの多くは、メソッド・ジェネレータです。
プロパティの振る舞いクラスは、システム・クラスです。プロパティの振る舞いは、指定、または修正できません。
例えば、3 つのプロパティでクラス Person を定義する場合、以下のようになります。
Class MyApp.Person Extends %Persistent
{
Property Name As %String;
Property Age As %Integer;
Property DOB As %Date;
}
コンパイルされた Person クラスは、各プロパティに対して自動的に生成された一連のメソッドを持ちます。これらのメソッドは、プロパティと関連するデータ型クラスからも、システムの Property クラスからも継承されます。生成されたこれらのメソッド名は、プロパティ名に継承クラスからのメソッド名を連結した名前です。例えば、DOB プロパティに関連付けられたメソッドでは以下のようになります。
Set x = person.DOBIsValid(person.DOB)
Write person.DOBLogicalToDisplay(person.DOB)
IsValid() はプロパティ・クラスのメソッドで、LogicalToDisplay() は %DateOpens in a new tab データ型クラスのメソッドです。
リテラル・プロパティのプロパティ・アクセサ
オブジェクト・プロパティを参照する InterSystems IRIS のドット構文は、値を取得および設定するアクセサ・メソッドのセットへのインタフェースです。それぞれの非計算プロパティについては、コードから oref.Prop (oref はオブジェクト、Prop はプロパティ) を参照すると、常にシステム提供の PropGet() メソッド、または PropSet() メソッドが呼び出されたかのように実行されます。以下に例を示します。
Set person.DOB = x
上記のコードは、以下のメソッドが呼び出されたかのように振る舞います。
Do person.DOBSet(x)
以下のコードは、
Write person.Name
以下のように振る舞います。
Write person.NameGet()
多くの場合、実際には PropGet() メソッドや PropSet() メソッドは存在しません。最善のパフォーマンスを得るために、単純なプロパティへのアクセスは InterSystems IRIS 仮想マシン内で直接実装されます。ただし、プロパティがオブジェクトタイプまたは多次元でない限り、特定のプロパティのために PropGet() メソッドと PropSet() メソッドを用意できます。これらのメソッドを定義すると、そのメソッドは実行時に、システムによって自動的に呼び出されます。以下のセクションでは、これらのアクセッサ・メソッドを定義する方法を説明します。カスタム・メソッド内では、アプリケーションに必要な任意の特別な処理を実行できます。
スタジオの [新規プロパティ・ウィザード] の最後の画面には、カスタム Get() メソッド、Set() メソッド、またはその両方を生成するオプションが用意されています。これらのオプションを使用すると、スタジオは適切なシグニチャと共にスタブ・メソッドを定義します。
PropGet() メソッドおよび PropSet() メソッドを使用してオブジェクトのプロパティにアクセスするには、オブジェクトをメモリにロードする必要があります。一方、PropGetStored() メソッドを使用すると、保存されているオブジェクトのプロパティ値をディスクから直接取得できます。オブジェクト全体をメモリにロードする必要はありません。例えば、ID が 44 の個人の名前を書き込むには、以下のコードを使用できます。
Write ##class(MyApp.Person).NameGetStored(44)
オブジェクト値プロパティのプロパティ・アクセサ
すべての参照プロパティに対しては、SetObject() (OID 値を使用する) メソッドと SetObjectId() (ID 値を使用する) メソッドがあります。例えば、特定の保存された Person オブジェクトを Car オブジェクトの持ち主として割り当てるには、以下のコードを使用します。
Do car.OwnerSetObjectId(PersonId)
car は Car オブジェクトの OREF、PersonId は保存された Person オブジェクトの ID です。
GetObject() メソッドと GetObjectId() メソッドも使用できます。これらのメソッドは、それぞれ参照プロパティに関連付けられている OID または ID を取得します。まとめると、以下のメソッドがあります。
-
GetObject() — プロパティに関連付けられている OID を取得します。プロパティの名前が prop の場合、メソッド名は propGetObject() です。
-
GetObjectId() — プロパティに関連付けられている ID を取得します。プロパティの名前が prop の場合、メソッド名は propGetObjectId() です。
-
SetObject() — プロパティに関連付けられている OID を設定します。プロパティの名前が prop の場合、メソッド名は propSetObject() です。
-
SetObjectId() — プロパティに関連付けられている ID を設定します。プロパティの名前が prop の場合、メソッド名は propSetObjectId() です。
プロパティ・ゲッター・メソッドのオーバーライド
プロパティのゲッター・メソッドをオーバーライドするには、そのプロパティを含むクラスを変更して、以下のようなメソッドを追加します。
-
PropertyNameGet という名前にする必要があります。PropertyName は、対応するプロパティの名前です。
-
引数を取りません。
-
返りタイプは、プロパティのタイプと同じにする必要があります。
-
プロパティの値を返す必要があります。
-
このプロパティの値を参照するには、このメソッドで変数 i%PropertyName を使用する必要があります。この名前では大文字と小文字が区別されます。
Important:特定のプロパティに対するこのゲッター・メソッド内では、そのプロパティの値を参照するために、..PropertyName 構文を使用してはいけません。そのようにすると、再帰的な参照の連続により <FRAMESTACK> エラーを招くことになります。ただし、その他のプロパティを参照する場合には ..PropertyName を使用できます。このようにしても、再帰が発生することがないためです。
変数 i%PropertyName はインスタンス変数です。インスタンス変数の詳細は、"i%PropertyName" を参照してください。
オブジェクトタイプ・プロパティまたは多次元プロパティのアクセサ・メソッドに対するオーバーライドはサポートされていません。また、メソッド名の最大長が 220 文字なので、長さが 218 文字、219 文字、または 220 文字の名前を持つプロパティのアクセサ・メソッドは作成できません。
以下に、HasValue という名前のプロパティ (タイプ %BooleanOpens in a new tab) のセッター・メソッドの例を示します。
Method HasValueGet() As %Boolean
{
If ((i%NodeType="element")||(i%NodeType="")) Quit 0
Quit 1
}
プロパティ・セッター・メソッドのオーバーライド
プロパティのセッター・メソッドをオーバーライドするには、そのプロパティを含むクラスを変更して、以下のようなメソッドを追加します。
-
PropertyNameSet という名前にする必要があります。PropertyName は、対応するプロパティの名前です。
-
1 つの引数を受け取ります。この引数にはプロパティの値を格納します。
具体的には、SET コマンドで指定された値です (プロパティが設定されている場合)。
-
%StatusOpens in a new tab 値を返す必要があります。
-
このプロパティの値を設定するには、このメソッドで変数 i%PropertyName を設定する必要があります。この名前では大文字と小文字が区別されます。
Important:特定のプロパティに対するこのセッター・メソッド内では、そのプロパティの値を参照するために、..PropertyName 構文を使用してはいけません。そのようにすると、再帰的な参照の連続により <FRAMESTACK> エラーを招くことになります。ただし、その他のプロパティを参照する場合には ..PropertyName を使用できます。このようにしても、再帰が発生することがないためです。
変数 i%PropertyName はインスタンス変数です。インスタンス変数の詳細は、"i%PropertyName" を参照してください。
オブジェクトタイプ・プロパティまたは多次元プロパティのアクセサ・メソッドに対するオーバーライドはサポートされていません。また、メソッド名の最大長が 220 文字なので、長さが 218 文字、219 文字、または 220 文字の名前を持つプロパティのアクセサ・メソッドは作成できません。
例えば、MyProp のタイプは %StringOpens in a new tab だとします。以下のセッター・メソッドを定義できます。
Method MyPropSet(value as %String) As %Status
{
if i%MyProp="abc" {
set i%MyProp="corrected value"
}
quit $$$OK
}
以下に示す別の例は、DefaultXmlns という名前のプロパティのセッター・メソッド (タイプ %StringOpens in a new tab) です。
Method DefaultXmlnsSet(value As %String) As %Status
{
set i%DefaultXmlns = value
If ..Namespaces'="" Set ..Namespaces.DefaultXmlns=value
quit $$$OK
}
この例では、同じオブジェクトの Namespaces プロパティを参照するために、..PropertyName 構文を使用しています。この使用法は、再帰が発生しないため、エラーになりません。
カスタム・アクセサ・メソッドによるオブジェクト値プロパティの定義
前述したように、オブジェクトタイプ・プロパティのアクセサ・メソッドに対するオーバーライドはサポートされていません。オブジェクト値を保持するプロパティを定義する必要があり、カスタム・アクセサ・メソッドを定義する必要もある場合は、%RawString. タイプでプロパティを定義します。これは、オブジェクト・クラスではなく汎用クラスであるため、このプロパティのアクセサ・メソッドはオーバーライドできます。このプロパティを使用する場合は、目的のクラスのインスタンスと等しくなるように設定します。
例えば、以下のクラスにはプロパティ Zip が含まれます。その公式の型は %RawStringOpens in a new tab です。プロパティの説明は、そのプロパティが Sample.USZipCode のインスタンスであることを示します。このクラスでは、ZipGet() および ZipSet() プロパティ・メソッドも定義されます。
Class PropMethods.Demo Extends %Persistent
{
/// Timestamp for viewing Zip
Property LastTimeZipViewed As %TimeStamp;
/// Timestamp for changing Zip
Property LastTimeZipChanged As %TimeStamp;
/// When setting this property, set it equal to instance of Sample.USZipCode.
/// The type is %RawString rather than Sample.USZipCode, so that it's possible
/// to override ZipGet() and ZipSet().
Property Zip As %RawString;
Method ZipGet() As %RawString [ ServerOnly = 1 ]
{
// get id, swizzle referenced object
set id = i%Zip
if (id '= "") {
set zip = ##class(Sample.USZipCode).%OpenId(id)
set ..LastTimeZipViewed = $zdt($zts)
}
else {
set zip = ""
}
return zip
}
Method ZipSet(zip As %RawString) As %Status [ ServerOnly = 1 ]
{
// set i% for new zip
if ($isobject(zip) && zip.%IsA("Sample.USZipCode")) {
set id = zip.%Id()
set i%Zip = id
set ..LastTimeZipChanged = $zdt($zts)
}
else {
set i%Zip = ""
}
quit $$$OK
}
}
以下のターミナル・セッションは、このクラスの使用法を示しています。
SAMPLES>set demo=##class(PropMethods.Demo).%New()
SAMPLES>write demo.LastTimeZipChanged
SAMPLES>set zip=##class(Sample.USZipCode).%OpenId(10001)
SAMPLES>set demo.Zip=zip
SAMPLES>w demo.LastTimeZipChanged
10/14/2015 19:21:08