第二十四章 Caché ObjectScript对象特性功能
相对点语法(..)
相对点语法(..)提供了一种在当前上下文中引用方法或属性的机制。实例方法或属性的上下文是当前实例。类方法的上下文是在其中实现该方法的类。不能在类方法中使用相对点语法来引用属性或实例方法,因为它们需要实例上下文。
注意:类方法实际就是静态方法。
例如,假设有一个类型为%Integer的Bricks属性:
Property Bricks As %Integer;
然后,CountBricks()方法可以使用相对点语法引用Bricks:
Method CountBricks()
{
Write "There are ",..Bricks," bricks.",!
}
同样,WallCheck()方法可以引用CountBricks()和Bricks:
Method WallCheck()
{
Do ..CountBricks()
If ..Bricks < 100 {
Write "Your wall will be small."
}
}
##class语法允许执行以下操作:
class语法允许执行以下操作:
- 调用类方法 没有类的现有实例或打开的实例时。
- 铸造方法 从一个类作为另一个方法。
- 访问类参数
注意:## class不区分大小写。
调用类方法
要调用类方法,语法可以是以下之一:
Do ##class(Package.Class).Method(Args)
Set localname = ##class(Package.Class).Method(Args)
将## class用作表达式的一部分也是有效的,例如
Write ##class(Class).Method(args)*2
而不将变量设置为等于返回值。
在创建新实例时经常使用此语法:
Set LocalInstance = ##class(Package.Class).%New()
铸造方法
要将一个类的方法转换为另一类的方法,语法可以是以下之一:
Do ##class(Package.Class1)Class2Instance.Method(Args)
Set localname = ##class(Package.Class1)Class2Instance.Method(Args)
可以同时转换类方法和实例方法。
例如,假设两个类MyClass.Up和MyClass.Down都具有Go()方法。
对于MyClass.Up,此方法如下
Method Go()
{
Write "Go up.",!
}
对于MyClass.Down,Go()方法如下:
Method Go()
{
Write "Go down.",!
}
然后,可以创建MyClass.Up的实例,并使用它来调用MyClass.Down.Go方法:
Set LocalInstance = ##class(MyClass.Up).%New()
Do ##class(MyClass.Down)LocalInstance.Go()
Go down.
引用其他方法的更通用的方法是CLASSMETHOD函数,它们分别是实例方法和类方法。
访问类参数
要访问类参数,可以使用以下表达式:
##class(Package.Class).#PARMNAME
其中Package.Class是类的名称,而PARMNAME是参数的名称。例如:
w ##class(%XML.Adaptor).#XMLIGNOREINVALIDTAG
$this语法
$this变量提供了当前实例的OREF的句柄,例如用于将其传递给另一个类或让另一个类引用当前实例的属性或方法。当实例引用其自身的属性或方法时,相对点语法更快,因此是首选。
注意:this,THIS或任何其他变体都具有相同的值。
例如,假设有一个带有Accounting.Order类和Accounting.Utils类的应用程序。
Accounting.Order.CalcTax()方法调用Accounting.Utils.GetTaxRate()和Accounting.Utils.GetTaxableSubtotal()方法,将当前实例的城市和州值传递给GetTaxRate()方法,并传递项目列表向GetTaxableSubtotal()订购了与税收相关的信息。然后,CalcTax()使用返回的值来计算订单的营业税。因此,其代码类似于:
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属性的值。第三行上的操作类似于第一行上的操作。
然后,在Accounting.Utils类中,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
}
GetTaxableSubtotal()方法还使用实例的句柄来查看其属性并设置其TaxableSubtotal属性的值。
因此,在Caché终端上通过为Accounting.Order类的MyOrder实例调用CalcTax()方法而得到的输出将类似于:
>Do MyOrder.CalcTax()
The tax rate for Cambridge, MA is 5%
The taxable subtotal for this order is $79.82
The tax for this order is $3.99
##super 语法
子类方法重写了父类方法。在子类方法中,可以使用##super()语法来调用重写的父类方法。
注意:##super不区分大小写。还要注意,与本章中的其他功能不同,##super()在Basic方法以及ObjectScript方法中均可用。
例如,类MyClass.Down继承了MyClass.Up并重写了Simple类方法。如果MyClass.Up.Simple()的代码为:
ClassMethod Simple()
{
Write "Superclass.",!
}
MyClass.Down.Simple()的代码是
ClassMethod Simple()
{
Write "Subclass.",!
Do ##super()
}
那么子类方法MyClass.Down.Simple()的输出为:
>Do ##Class(MyClass.Down).Simple()
Subclass.
Superclass.
调用##super关键字的影响
super仅影响当前方法调用。如果该方法进行任何其他调用,则这些调用是相对于当前对象或类,而不是父类。例如,假设MyClass.Up具有MyName()和CallMyName()方法:
Class MyClass.Up Extends %Persistent
{
ClassMethod CallMyName()
{
Do ..MyName()
}
ClassMethod MyName()
{
Write "Called from MyClass.Up",!
}
}
并且MyClass.Down重写了这些方法,如下所示:
Class MyClass.Down Extends MyClass.Up
{
ClassMethod CallMyName()
{
Do ##super()
}
ClassMethod MyName()
{
Write "Called from MyClass.Down",!
}
}
然后调用CallMyName()方法将得到以下结果:
USER>d ##class(MyClass.Up).CallMyName()
Called from MyClass.Up
USER>d ##class(MyClass.Down).CallMyName()
Called from MyClass.Down
MyClass.Down.CallMyName()与MyClass.Up.CallMyName()的输出有所不同,因为其CallMyName()方法包括## super,因此调用MyClass.Up.CallMyName()方法,然后调用重写了MyClass.Down。 MyName()方法。
##super和方法参数
super也可以与接受参数的方法一起使用。如果子类方法未为参数指定默认值,请确保该方法通过引用将参数传递给超类。例如,假设超类(MyClass.Up.SelfAdd())中方法的代码为:
ClassMethod SelfAdd(Arg As %Integer)
{
Write Arg,!
Write Arg + Arg
}
输出:
>Do ##Class(MyClass.Up).SelfAdd(2)
2
4
子类(MyClass.Down.SelfAdd())中的方法使用## super并通过引用传递参数:
ClassMethod SelfAdd(Arg1 As %Integer)
{
Do ##super(.Arg1)
Write !
Write Arg1 + Arg1 + Arg1
}
输出:
>Do ##Class(MyClass.Up).SelfAdd(2)
2
4
6
在MyClass.Down.SelfAdd()中,注意参数名称之前的点。如果忽略了这一点,并且在不提供参数的情况下调用了该方法,则会收到错误。
动态访问对象
Caché提供了一些支持通用对象处理的功能。通过允许在运行时计算对类及其方法或属性之一的引用来做到这一点。(这在Java中被称为反射)。
- $CLASSNAME — 返回类名称.
- $CLASSMETHOD — 支持对类方法的调用.
- $METHOD — 支持对实例方法的调用.
- $PARAMETER — 返回指定类的类参数的值.
- $PROPERTY — 支持对实例的特定属性的引用.
函数名称在此处以所有大写字母显示,但实际上它们不区分大小写。
$CLASSNAME
此函数返回类的名称
$CLASSNAME(Instance)
其中Instance是OREF。
$CLASSMETHOD
该函数在指定的类中执行命名类方法。
$CLASSMETHOD (Classname, Methodname, Arg1, Arg2, Arg3, ... )
- Classname 存在的类
- Methodname 类方法
- Arg1, Arg2, Arg3, ... 参数
$METHOD
函数执行实例方法
$METHOD (Instance, Methodname, Arg1, Arg2, Arg3, ... )
- Classname 存在的类
- Methodname 类方法
- Arg1, Arg2, Arg3, ... 参数
$PARAMETER
此函数返回指定类的类参数的值。
$PARAMETER(Instance,Parameter)
- Instance 类的完全限定名称或该类实例的OREF。
- Parameter 给定类的参数。
$PROPERTY
此函数获取或设置指定类的实例中的属性的值。如果属性是多维的,则在属性名称之后的以下参数将用作访问属性值的索引。
$PROPERTY (Instance, PropertyName, Index1, Index2, Index3... )
- Instance 类实例的OREF。
- PropertyName 实例在第一个参数中指定的类的属性。
- Index1, Index2, Index3, ... 对于多维属性,索引到该属性表示的数组中。
i% 语法
本节提供有关实例变量的其他信息。除非你为属性覆盖访问器方法,否则无需引用这些变量。
创建任何类的实例时,Caché会为该类的每个未计算的属性创建一个实例变量。实例变量保存属性的值。对于属性PropName,实例变量名为i%PropName,并且此变量名称区分大小写。这些变量在该类的任何实例方法中都可用。
例如,如果一个类具有属性Name和DOB,则实例变量i%Name和i%DOB在该类的任何实例方法中都可用。
在内部,Caché还使用名称为r%PropName和m%PropName之类的其他实例变量,但不支持直接使用这些变量。
实例变量具有为其分配的进程专用的内存中存储。请注意,这些变量未保存在局部变量符号表中,并且不受Kill命令影响。
..#语法
..#语法允许从同一类的方法内引用类参数。
例如,如果类定义包括以下参数和方法:
Parameter MyParam = 22;
和以下方法:
ClassMethod WriteMyParam()
{
Write ..#MyParam
}
然后,WriteMyParam()方法以MyParam参数的值作为参数来调用Write命令。