Caché Objects | 第四章 | Registered Objects 的使用

%RegisteredObject 类是 Caché 中的基本对象 API 。对象类是继承自 %RegisteredObject 的任何类。使用对象类,可以执行以下操作:

  • 创建类的实例。这些实例称为对象。
  • 设置这些对象的属性。
  • 调用这些对象的方法(实例方法)。

%Persistent%SerialObject%RegisteredObject 的子类.

一、OREF 基础知识

创建对象时,系统会创建一个内存中结构,该结构包含有关该对象的信息,并创建一个 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.
  1. 在第一步中,我们调用Sample.Person类的%New()方法。它创建一个对象并返回一个指向该对象的OREF。我们将变量person设置为等于此OREF
  2. 在下一步中,我们设置对象的Name属性。
  3. 在第三步中,我们调用对象的PrintPerson()实例方法。

注意OREF是瞬态的;该值仅在对象位于内存中时存在,并且不能保证在不同的调用中保持不变。

1.1 INVALID OREF 错误

在简单表达式中,如果尝试设置属性、访问属性或调用非 OREF 变量的实例方法,则会收到<INVALID OREF>错误。例如:

DHC-APP>w p.name
 
W p.name
^
<INVALID OREF>

1.2 判断是否为OREF

Caché 提供了一个函数 $ISOBJECT,您可以使用它来测试给定变量是否具有 OREF。如果变量包含 OREF,则此函数返回 1,否则返回 0

if ($ISOBJECT(P)) w p.name

1.3 OREF、作用域和内存

任何给定的 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超出了作用域范围,第二个对象被销毁。

1.4 删除 OREF

如果需要,要删除 OREF,请使用 KILL 命令:

kill OREF

其中 OREF 是包含 OREF 的变量。此命令删除变量。如果没有对该对象的进一步引用,则此命令还会从内存中删除该对象。

1.5 OREF、SET 命令和系统函数

对于某些系统函数(例如,$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 类提供用于确定其继承的方法.

4.1 %Extends()

若要检查对象是否继承自特定超类,请调用其 %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

4.2 %IsA()

若要检查对象是否具有特定类作为其主超类,请调用其 %IsA() 方法,并将该超类的名称作为参数传递。如果此方法返回 1,则该对象确实具有给定的类作为其主超类。

4.3 %ClassName()和最具体的类型类 (MSTC)

尽管对象可能是多个类的实例,但它始终具有最具体的类型类 (MSTC: the Most Specific Type Class)。当一个对象是该类的实例而不是该类的任何子类的实例时,该类被称为该对象的最具体类型。

例如,研究生继承自学生,学生继承自人类,对于由命令创建的实例:

set MyInstance1 = ##class(MyPackage.Student).%New()
 set MyInstance2 = ##class(MyPackage.GradStudent).%New()
  • MyInstance1 具有学生作为其 MSTC,因为它是两者的实例:人和学生,但不是研究生。
  • MyInstance2 具有研究生作为其 MSTC,因为它是三者的实例:研究生,学生和人.

以下规则也适用于对象的 MSTC:

  • 对象的 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 对象不同。NewPersonperson 的克隆(或者更准确地说,这些变量是指向单独但相同对象的指针)。

相比之下,请考虑以下终端会话:

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 的计算结果为关联类中属性方法的名称。此外,subscript1subscript2subscript3 是属性的任何下标的值; 仅为多维属性指定这些属性。

七、调用实例的方法

若要调用实例的方法,可以执行以下任一操作:

  • 创建关联类的实例,并使用点语法调用该实例的方法。
  • 实例方法中,若要调用该类的另一个实例方法(可以是继承的方法),请使用以下表达式:
do ..MethodName()
set value=..MethodName(args)
  • 若要执行实例方法,其中方法名称直到运行时才确定,请使用 $METHOD 函数:
$METHOD(oref, methodname, Arg1, Arg2, Arg3, ... )

其中 oref 是 OREF,methodname 的计算结果为关联类中实例方法的名称,Arg1Arg2Arg3 等是该方法的任何参数。

八、从实例中获取类名

若要获取类的名称,请使用 $CLASSNAME 函数:

$CLASSNAME(oref)

其中 oref 是 OREF。

九、$this 变量(当前实例)

$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 语法将当前对象传递给该方法。
  • 该方法的第二行使用 相对点语法.. 来获取 CityState 属性的值。
  • 第三行上的操作与第一行上的操作类似。

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
}

十、i%PropertyName(实例变量)

本节介绍实例变量,除非重写属性的访问器方法,否则不需要引用这些变量;请参阅“使用和重写属性方法”一章。

创建任何类的实例时,系统都会为该类的每个非计算属性创建一个实例变量。实例变量保存属性的值。对于属性 PropName,实例变量命名为 i%PropName,并且此变量名称区分大小写。这些变量在类的任何实例方法中都可用

例如,如果某个类具有属性 NameDOB,则实例变量 i%Namei%DOB 在该类的任何实例方法中都可用。

在内部,Caché 还使用名称为 r%PropNamem%PropName 的其他实例变量,但不支持直接使用这些变量。

实例变量分配了进程专用的内存中存储。请注意,这些变量不保存在局部变量符号表中,并且不受 Kill 命令的影响

十一、验证对象

%RegisteredObject 类提供了一种验证实例属性的方法。如果满足以下所有条件,则对象有效:

  • 所有必需的属性都有值。
    (若要使属性成为必需属性,请使用 Required 关键字。
    如果属性的类型为 %Stream,则该流不能为 null 流。也就是说,如果 %IsNull() 方法返回 0,则认为该属性具有值。
  • 每个属性的值(如果不是 null)对于关联的属性定义有效。
    例如,如果属性的类型为 %Boolean,则值“abc”无效,但值 01 有效。
  • 每个文本属性的值(如果不是 null)都遵循属性定义定义的所有约束。术语约束是指对属性值应用约束的任何属性关键字。例如,MAXLEN、MAXVAL、MINVAL、VALUELIST 和 PATTERN 都是约束;请参阅“定义和使用文本属性”一章。例如,对于 %String 类型的属性,默认值为 MAXLEN,这会将属性的长度限制为不超过 50 个字符。
  • (对于对象值属性)对象的所有属性都遵循上述规则。
  • (对于持久性对象)该对象不违反任何 SQL 约束,例如给定字段的唯一性约束。
  • 若要确定给定对象是否有效,请调用其 %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"
    }
  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值