%RegisteredObject 类是 Caché 中的基本对象 API 。对象类是继承自 %RegisteredObject
的任何类。使用对象类,可以执行以下操作:
类 %Persistent
和 %SerialObject
是 %RegisteredObject
的子类.
创建对象时,系统会创建一个内存中结构,该结构包含有关该对象的信息,并创建一个 OREF
(对象引用),它是指向该结构的指针。
对象类提供了几种创建 OREF 的方法。使用任何对象类时,都会广泛使用 OREF。在指定对象的属性值、访问对象的属性值以及调用对象的实例方法时,可以使用它们。请看以下示例:
SAMPLES>set person=##class(Sample.Person).%New()
SAMPLES>set person.Name="Carter,Jacob N."
SAMPLES>do person.PrintPerson()
Name: Carter,Jacob N.
Sample.Person
类的%New()
方法。它创建一个对象并返回一个指向该对象的OREF
。我们将变量person
设置为等于此OREF
。PrintPerson()
实例方法。注意:OREF
是瞬态的;该值仅在对象位于内存中时存在,并且不能保证在不同的调用中保持不变。
在简单表达式中,如果尝试设置属性、访问属性或调用非 OREF
变量的实例方法,则会收到
错误。例如:
DHC-APP>w p.name
W p.name
^
<INVALID OREF>
Caché
提供了一个函数 $ISOBJECT
,您可以使用它来测试给定变量是否具有 OREF
。如果变量包含 OREF
,则此函数返回 1
,否则返回 0
。
if ($ISOBJECT(P)) w p.name
任何给定的 OREF
都是指向内存中对象的指针,其他 OREF 也可能指向该对象。也就是说,OREF
(变量)与内存中对象不同(尽管在实践中,术语 OREF 和对象经常互换使用)。
请注意以下几点:
OREF
仅在特定名称空间内有效;因此,如果存在现有的OREF并且当前命名空间发生了更改,则来自先前命名空间的任何OREF都不再有效。如果尝试使用其他命名空间中的OREF
,可能不会立即出现错误,但结果不能被视为有效或可用,并可能在当前命名空间中造成灾难性的结果。Caché
自动管理内存中的结构,如下所示。对于每个内存中的对象,Caché都会维护一个引用计数——对该对象的引用数。每当将变量或对象属性设置为引用对象时,其引用计数都会自动递增。当一个变量停止引用一个对象时(如果它超出范围、被终止或被设置为新值),该对象的引用计数就会递减。当此计数为0时,对象将自动销毁(从内存中删除),并调用其%OnClose()
方法(如果存在)。例如,请考虑以下方法:
Method Test()
{
Set person = ##class(Sample.Person).%OpenId(1)
Set person = ##class(Sample.Person).%OpenId(2)
}
Sample.Person
的实例。并将对它的引用放置到变量person
中。person
的值替换为对它的引用。此时,第一个对象不再被引用并被销毁。person
超出了作用域范围,第二个对象被销毁。如果需要,要删除 OREF,请使用 KILL 命令:
kill OREF
其中 OREF
是包含 OREF 的变量。此命令删除变量。如果没有对该对象的进一步引用,则此命令还会从内存中删除该对象。
对于某些系统函数(例如,$Piece
、$Extract
和 $List
),Caché 支持可用于修改现有值的替代语法。此语法将函数与 SET
命令组合在一起,如下所示:
SET function_expression = value
其中 function_expression
是对系统函数的调用,带有参数,value
是值。例如,以下语句将颜色列表字符串的第一部分设置为等于“Magenta”:
SET $PIECE(colorlist,",",1)="Magenta"
USER>s a="1,2,3"
USER>s $p(a,",",1)=4
USER>w a
4,2,3
若要创建给定对象类的新实例,请使用该类的类方法 %New()
。此方法创建一个对象并返回一个 OREF
。示例如下:
Set person = ##class(MyApp.Person).%New()
%New()
方法接受一个参数,默认情况下会忽略该参数。如果存在,则此参数将传递给类的 %OnNew()
回调方法(如果已定义)。如果定义了 %OnNew()
,则它可以使用该参数以某种方式初始化新创建的对象。有关详细信息,请参阅“实现回调方法”。
如果有复杂的要求,这些要求会影响如何创建给定类的新对象,则可以提供用于创建该类实例的替代方法。这样的方法将调用 %New()
,然后根据需要初始化对象的属性。这种方法有时称为工厂方法。
WRITE
命令为 OREF
写入以下形式的输出:
n@Classname
其中 Classname
是类的名称,n
是一个整数,指示该类在内存中的特定实例。例如:
SAMPLES>write p
8@Sample.Person
如果将 ZWRITE
命令与 OREF
一起使用,则 Caché
将显示有关关联对象的详细信息:
SAMPLES>zwrite p
p=<OBJECT REFERENCE>[8@Sample.Person]
+----------------- general information ---------------
| oref value: 1
| class name: Sample.Person
| %%OID: $lb("3","Sample.Person")
| reference count: 2
+----------------- attribute values ------------------
| %Concurrency = 1 <Set>
| DOB = 33589
| Name = "Clay,George O."
| SSN = "480-57-8360"
+----------------- swizzled references ---------------
| i%FavoriteColors = "" <Set>
| r%FavoriteColors = "" <Set>
| i%Home = $lb("5845 Washington Blvd","St Louis","NM",55683) <Set>
| r%Home = "" <Set>
| i%Office = $lb("3413 Elm Place","Pueblo","WI",98532) <Set>
| r%Office = "" <Set>
| i%Spouse = ""
| r%Spouse = ""
+-----------------------------------------------------
请注意,此信息显示对象属性的类名、OID
、引用计数和当前值(在内存中)。在 swizzled
引用部分中,名称以 i%
开头的项是实例属性
。(名称以 r%
开头的项目仅供内部使用)
给定一个对象,%RegisteredObject
类提供用于确定其继承的方法.
若要检查对象是否继承自特定超类,请调用其 %Extends()
方法,并将该超类的名称作为参数传递。如果此方法返回 1,则实例继承自该类。如果它返回 0,则实例不会从该类继承。例如:
SAMPLES>set person=##class(Sample.Person).%New()
SAMPLES>w person.%Extends("%RegisteredObject")
1
SAMPLES>w person.%Extends("Sample.Person")
1
SAMPLES>w person.%Extends("Sample.Employee")
0
若要检查对象是否具有特定类作为其主超类,请调用其 %IsA()
方法,并将该超类的名称作为参数传递。如果此方法返回 1,则该对象确实具有给定的类作为其主超类。
尽管对象可能是多个类的实例,但它始终具有最具体的类型类 (MSTC: the Most Specific Type Class
)。当一个对象是该类的实例而不是该类的任何子类的实例时,该类被称为该对象的最具体类型。
例如,研究生继承自学生,学生继承自人类,对于由命令创建的实例:
set MyInstance1 = ##class(MyPackage.Student).%New()
set MyInstance2 = ##class(MyPackage.GradStudent).%New()
MyInstance1
具有学生作为其 MSTC,因为它是两者的实例:人和学生,但不是研究生。MyInstance2
具有研究生作为其 MSTC,因为它是三者的实例:研究生,学生和人.以下规则也适用于对象的 MSTC:
若要确定对象的 MSTC,请使用 %ClassName()
方法,该方法继承自 %RegisteredObject
classmethod %ClassName(fullname As %Boolean) as %String
其中 fullname
是一个布尔参数,其中 1 指定方法返回包名和类名,0(默认值)指定方法仅返回类名。例如:
write myinstance.%ClassName(1)
同样,可以使用 %PackageName()
仅获取包的名称。
若要克隆对象,请调用该对象的 %ConstructClone()
方法。此方法创建新的 OREF
。
以下终端会话演示了这一点:
SAMPLES>set person=##class(Sample.Person).%OpenId(1)
SAMPLES>set NewPerson=person.%ConstructClone()
SAMPLES>w
NewPerson=<OBJECT REFERENCE>[2@Sample.Person]
person=<OBJECT REFERENCE>[1@Sample.Person]
SAMPLES>
在这里,您可以看到 NewPerson
变量使用的 OREF
与原始 person
对象不同。NewPerson
是 person
的克隆(或者更准确地说,这些变量是指向单独但相同对象的指针)。
相比之下,请考虑以下终端会话:
SAMPLES>set person=##class(Sample.Person).%OpenId(1)
SAMPLES>set NotNew=person
SAMPLES>w
NotNew=<OBJECT REFERENCE>[1@Sample.Person]
person=<OBJECT REFERENCE>[1@Sample.Person]
请注意,此处两个变量都引用相同的 OREF
。也就是说,NotNew
不是人的克隆。
若要引用实例的属性,可以执行以下任一操作:
set value=..PropName
write ..PropName
$PROPERTY
函数。如果属性是多维的,则属性名称后面的以下参数将用作访问属性值的索引。签名是:$PROPERTY (oref, propertyName, subscript1, subscript2, subscript3... )
其中 oref
是 OREF,则 propertyName
的计算结果为关联类中属性方法的名称。此外,subscript1
、subscript2
、subscript3
是属性的任何下标的值; 仅为多维属性指定这些属性。
若要调用实例的方法,可以执行以下任一操作:
do ..MethodName()
set value=..MethodName(args)
$METHOD
函数:$METHOD(oref, methodname, Arg1, Arg2, Arg3, ... )
其中 oref
是 OREF,methodname
的计算结果为关联类中实例方法的名称,Arg1
、Arg2
、Arg3
等是该方法的任何参数。
若要获取类的名称,请使用 $CLASSNAME
函数:
$CLASSNAME(oref)
其中 oref
是 OREF。
$this
语法提供当前实例的 OREF
句柄,例如,用于将其传递给另一个类或另一个类引用当前实例的方法的属性。当实例引用其属性或方法时,相对点语法速(..
)度更快,因此是首选。
注意:
$this
不区分大小写;因此,$this
、$This
、$THIS
或任何其他变体都具有相同的值。
Method CalcTax() As %Numeric
{
Set TaxRate = ##class(Accounting.Utils).GetTaxRate($this)
Write "The tax rate for ",..City,", ",..State," is ",TaxRate*100,"%",!
Set TaxableSubtotal = ##class(Accounting.Utils).GetTaxableSubTotal($this)
Write "The taxable subtotal for this order is $",TaxableSubtotal,!
Set Tax = TaxableSubtotal * TaxRate
Write "The tax for this order is $",Tax,!
}
##Class
语法来调用另一个方法;它使用 $this
语法将当前对象传递给该方法。..
来获取 City
和 State
属性的值。GetTaxRate()
方法可以使用传入实例的句柄来获取各种属性的句柄,用于获取和设置其值.
ClassMethod GetTaxRate(OrderBeingProcessed As Accounting.Order) As %Numeric
{
Set LocalCity = OrderBeingProcessed.City
Set LocalState = OrderBeingProcessed.State
// code to determine tax rate based on location and set
// the value of OrderBeingProcessed.TaxRate accordingly
Quit OrderBeingProcessed.TaxRate
}
本节介绍实例变量,除非重写属性的访问器方法,否则不需要引用这些变量;请参阅“使用和重写属性方法”一章。
创建任何类的实例时,系统都会为该类的每个非计算属性创建一个实例变量。实例变量保存属性的值。对于属性 PropName
,实例变量命名为 i%PropName
,并且此变量名称区分大小写。这些变量在类的任何实例方法中都可用。
例如,如果某个类具有属性 Name
和 DOB
,则实例变量 i%Name
和 i%DOB
在该类的任何实例方法中都可用。
在内部,Caché
还使用名称为 r%PropName
和 m%PropName
的其他实例变量,但不支持直接使用这些变量。
实例变量分配了进程专用的内存中存储。请注意,这些变量不保存在局部变量符号表中,并且不受 Kill 命令的影响。
%RegisteredObject
类提供了一种验证实例属性的方法。如果满足以下所有条件,则对象有效:
Required
关键字。%Stream
,则该流不能为 null
流。也就是说,如果 %IsNull()
方法返回 0,则认为该属性具有值。null
)对于关联的属性定义有效。%Boolean
,则值“abc”
无效,但值 0
和 1
有效。%ValidateObject()
方法。如果此方法返回 1,则对象有效。如果它返回错误状态,则该对象无效。示例如下: #Include %occStatus
set person=##class(Sample.Person).%New()
set person.DOB="December 12 1990"
set status=person.%ValidateObject()
write !, "First try"
if $$$ISERR(status) {
do $system.OBJ.DisplayError(status)
} else {
write !, "Object is valid"
}
set person.Name="Ellsworth,Myra Q."
set person.SSN="000-00-0000"
set person.DOB=$zdateh("December 12 1990",5)
set status=person.%ValidateObject()
write !!, "Second try"
if $$$ISERR(status) {
do $system.OBJ.DisplayError(status)
} else {
write !, "Object is valid"
}