文章目录
-
第二十一章 Caché 使用和覆盖属性方法 - 简介
- 属性方法简介
- 文字属性的属性访问器
- 对象值属性的属性访问器
- 重写属性获取器方法
- 使用自定义访问器方法定义对象值属性
本章介绍属性方法,这是当使用OREF处理对象的属性时Caché使用的实际方法。
属性方法简介属性具有许多与之自动关联的方法。这些方法不是通过标准继承继承的。而是,它们使用特殊的属性行为机制为每个属性生成一系列方法。
每个属性都从两个地方继承一组方法:
-
%Property类,它提供某些内置行为,例如Get(),Set()和验证代码。
-
属性使用的数据类型类。这些方法很多都是方法生成器。
属性类是系统类。不能指定或修改属性行为。
例如,如果我们定义一个具有三个属性的Person类:
Class MyApp.Person Extends %Persistent
{
Property Name As %String;
Property Age As %Integer;
Property DOB As %Date;
}
编译的Person类具有为其每个属性自动生成的一组方法。这些方法继承自系统的Property类以及与该属性关联的数据类型类。这些生成的方法的名称是属性名称,该属性名称与继承的类中方法的名称连接在一起。
例如,与DOB属性关联的一些方法是:
/// d ##class(PHA.OP.MOB.Test).TestIsValid()
ClassMethod TestIsValid()
{
s person=##class(User.MyClass).%OpenId("1||2")
w person.DOB,!
Set x = person.DOBIsValid(person.DOB)
w x,!
Write person.DOBLogicalToDisplay(person.DOB),!
}
其中IsValid()是属性类的方法,而LogicalToDisplay()是%Date数据类型类的方法。
DHC-APP>d ##class(PHA.OP.MOB.Test).TestIsValid()
65442
1
03/04/2020
文字属性的属性访问器
引用对象属性的Caché点语法是一组用于检索和设置值的访问器方法的接口。对于每个未计算的属性,只要代码引用oref.Prop(其中oref是一个对象,而Prop是一个属性),它就会被执行,就像调用系统提供的PropGet()或PropSet()方法一样。例如:
Set person.DOB = x
就像调用以下方法一样:
Do person.DOBSet(x)
Write person.Name
等同于
Write person.NameGet()
在大多数情况下,看不到实际的PropGet()和PropSet()方法。直接在Caché虚拟机中实现对简单属性的访问,以实现最佳性能。但是,可以为特定属性提供PropGet()和PropSet()方法,只要该属性不是对象类型或多维的。如果定义这些方法,则Caché会在运行时自动调用它们。以下各节描述如何定义这些访问器方法。在自定义方法中,可以执行应用程序所需的任何特殊处理。方法,Caché在运行时自动调用它们。
请注意,Studio中“New Property Wizard”的最后一个屏幕提供了用于创建自定义Get()方法,Set()或两者的选项。如果使用这些选项,则Studio会使用适当的签名定义存根方法。
Property aaa As %String;
Method aaaGet() As %String [ ServerOnly = 1 ]
{
Quit ""
}
Method aaaSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
Quit $$$OK
}
对象值属性的属性访问器
对于每个引用属性,都有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()。
要覆盖属性的setter方法,请修改包含该属性的类并添加方法,如下所示:
- 它必须具有名称PropertyNameSet,其中PropertyName是相应属性的名称。
- 它采用一个参数,其中包含属性的值。
具体来说,这是设置属性时在SET命令中指定的值。
- 它必须返回%Status值。
- 若要设置此属性的值,此方法必须设置变量i%PropertyName。该名称区分大小写。
重要说明:在给定属性的此setter方法中,请勿使用…PropertyName语法引用该属性的值。如果尝试这样做,则结果是由一系列递归引用引起的错误。但是,可以使用…PropertyName引用其他属性。
变量i%PropertyName是一个实例变量
请注意,不支持为对象类型的属性或多维属性覆盖访问器方法。同样,由于方法名称的最大长度为220个字符,因此无法为长度为218、219或220个字符的属性创建访问器方法。
例如,假设MyProp的类型为%String。我们可以定义以下setter方法:
Method MyPropSet(value as %String) As %Status
{
if i%MyProp="abc" {
set i%MyProp="corrected value"
}
quit $$$OK
}
下面显示了另一个示例,它是名为DefaultXmlns的属性的设置方法,该属性的类型为%String:
Method DefaultXmlnsSet(value As %String) As %Status
{
set i%DefaultXmlns = value
If ..Namespaces'="" Set ..Namespaces.DefaultXmlns=value
quit $$$OK
}
请注意,此示例使用…PropertyName语法引用了同一对象的Namespaces属性。此用法不是错误,因为它不会引起任何递归。
Property name As %String;
Method nameGet() As %String [ ServerOnly = 1 ]
{
Quit i%name
}
Method nameSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
set i%name = Arg
if i%name ="yaoxin" {
set i%name="yaoxin is a bad guy"
}
Quit $$$OK
}
/// d ##class(PHA.OP.MOB.Test).TestGetSet()
ClassMethod TestGetSet()
{
s three=##class(PHA.OP.MOB.TestThree).%New()
d three.nameSet("yaoxin")
w "three.nameGet(): "_three.nameGet(),!
w "three.name: "_three.name,!
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestGetSet()
three.nameGet(): yaoxin is a bad guy
three.name: yaoxin is a bad guy
/// d ##class(PHA.OP.MOB.Test).TestGetSet()
ClassMethod TestGetSet()
{
s three=##class(PHA.OP.MOB.TestThree).%New()
d three.nameSet("yaoxin")
s three.name="yaoxin1"
w "three.nameGet(): "_three.nameGet(),!
w "three.name: "_three.name,!
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestGetSet()
three.nameGet(): yaoxin1
three.name: yaoxin1
使用自定义访问器方法定义对象值属性
如前所述,不支持为对象类型的属性覆盖访问器方法。如果需要定义一个保存对象值的属性,并且需要定义自定义访问器方法,请使用%CacheString类型定义该属性。 这不是对象类,而是泛型类,并且允许重写此属性的访问器方法。 使用该属性时,请将其设置为等于所需类的实例。
例如,以下类包含属性Zip,其形式类型为%CacheString。属性描述指示该属性旨在作为Sample.USZipCode的实例。该类还定义ZipGet()和ZipSet()属性方法。
Class PHA.OP.MOB.TestThree Extends %RegisteredObject
{
/// 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 %CacheString rather than Sample.USZipCode, so that it's possible
/// to override ZipGet() and ZipSet().
Property Zip As %CacheString;
Method ZipGet() As %CacheString [ ServerOnly = 1 ]
{
w "1",!
// get id, swizzle referenced object
set id = i%Zip
if (id '= "") {
w "2",!
set zip = ##class(Sample.USZipCode).%OpenId(id)
set ..LastTimeZipViewed = $zdt($zts,3)
}
else {
w "3",!
set zip = ""
}
return zip
}
Method ZipSet(zip As %CacheString) As %Status [ ServerOnly = 1 ]
{
w "4",!
// set i% for new zip
if ($isobject(zip) && zip.%IsA("Sample.USZipCode")) {
w "5",!
set id = zip.%Id()
set i%Zip = id
set ..LastTimeZipChanged = $zdt($zts,3)
}
else {
w "6",!
set i%Zip = ""
}
quit $$$OK
}
}
/// d ##class(PHA.OP.MOB.Test).TestSelfGetSet()
ClassMethod TestSelfGetSet()
{
set demo=##class(PHA.OP.MOB.TestThree).%New()
write demo.LastTimeZipChanged
set zip=##class(Sample.USZipCode).%OpenId(1)
set demo.Zip=zip
w demo.LastTimeZipChanged
}
/// d ##class(PHA.OP.MOB.Test).SaveZIp()
ClassMethod SaveZIp()
{
s zip = ##class(Sample.USZipCode).%New()
s zip.ZipCode=1
s zip.State="Y"
s zip.City ="天津"
s zip.ZipCode=1
s zip.Longitude=2
s zip.Latitude=3
s sc=zip.%Save()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestSelfGetSet()
4
5
03/05/2020 01:22:06
注意:
Method nameGet() As %String [ ServerOnly = 1 ]
{
Quit ..name
}
如果你在属性方法里使用…语法,则会报错! 因为…语法其实就是递归nameGet,陷入死循环
DHC-APP> d ##class(PHA.OP.MOB.Test).TestGetSet()
<FRAMESTACK>
改成 i%name 即可