方法
有两种方法:实例方法和类方法(在其他语言中称为静态方法)。在大多数情况下,方法是一个过程。
在方法定义中,可以包含影响方法行为方式的可选编译器关键字。以下列表显示了一些最常见的方法关键字:
ProcedureBlock
默认情况下,方法中使用的变量专用于该方法,因为默认情况下,所有方法都是过程块
。要将方法定义为非过程块
,请将 ProcedureBlock
关键字指定为 0,在这种情况下,此方法中的变量将是公共变量。例如:
Method MyMethod() [ ProcedureBlock = 0 ]
{
//implementation details
}
Language
在 Caché
中创建服务器端方法时,您可以选择实现语言。选项是 basic (Caché Basic)
、cache (Caché ObjectScript)
、mvbasic (MVBasic)
和 tsql (TSQL)
。
默认情况下,方法使用为类指定的 Language
关键字指定的语言。在大多数情况下,该关键字是 cache (Caché ObjectScript)。
Private
指定该方法是私有的。子类继承 Private
关键字的值,并且不能覆盖它。
默认情况下,方法是公共的,可以在任何地方访问。您可以将方法标记为私有(通过 Private 关键字)。如果您这样做:
- 它只能通过它所属的类的方法访问。
- 它不会出现在系统间类引用中,该类引用稍后将在“系统间类引用”中介绍。
但是,它是继承的,在定义该方法的类的子类中可用。
其他语言通常将此类方法称为受保护的方法。
在方法中,使用此处显示的语法来引用其他类成员:
..#PARAMETERNAME
在 InterSystems 提供的类中,按照约定,所有参数都以全部大写字母定义,但不需要您的代码执行此操作。
..methodname(arguments)
请注意,不能在类方法
中使用此语法来引用实例方法
。
仅在实例方法中
)要引用实例的属性,请使用如下表达式:..PropertyName
..PropertyNameA.PropertyNameB
这称为 Caché 点语法
。
此外,还可以调用对象值属性的实例方法。例如:
do ..PropertyName.MyMethod()
在方法中(或在routines
中),使用此处显示的语法来引用其他类中的方法:
若要调用类方法并访问其返回值,请使用如下所示的表达式:
##class(Package.Class).MethodName(arguments)
注意:##class 不区分大小写
。
若要调用实例方法,请创建一个实例(如下一章所述),然后使用如下所示的表达式调用该方法并访问其返回值:
instance.MethodName(arguments)
在实例方法中,有时需要引用当前实例本身,而不是实例的方法属性。例如,在调用某些其他代码时,可能需要将当前实例作为参数传递。在这种情况下,请使用特殊变量$THIS
来引用当前实例。
例如:
Set sc=header.ProcessService($this)
方法可以采用逗号分隔列表中的位置参数。对于每个参数,可以指定类型
和默认值
。
例如,下面是采用三个参数的方法的部分定义:
Method Calculate(count As %Integer, name, state As %String = "CA") as %Numeric
{
// ...
}
请注意,其中两个参数具有显式类型,另一个参数具有默认值。通常,最好显式指定每个参数的类型。
在 Caché ObjectScript
中,当您调用某个方法时,如果有合适的默认值,则可以跳过参数。例如,以下内容有效:
set myval=##class(mypackage.myclass).GetValue(,,,,,,4)
调用方法时,可以通过值或引用将变量值传递给该方法,就像对例程和子例程所做的那样。
方法的签名通常指示是否要通过引用传递参数。例如:
Method MyMethod(argument1, ByRef argument2, Output argument3)
ByRef
关键字指示应通过引用传递此参数。Output
关键字,您应通过引用传递此参数,并且该方法将忽略您最初为此参数提供的任何值。
同样,在定义方法时,可以在方法签名中使用 ByRef
和Output
关键字来通知其他用户该方法的使用方式。
您可以定义一个方法,使其接受可变数量的参数。例如:
ClassMethod MultiArg(Arg1... As %List)
{
Write "Invocation has ",
$GET(Arg1, 0),
" element",
$SELECT(($GET(Arg1, 0)=1):"", 1:"s"),
!
For i = 1 : 1 : $GET(Arg1, 0)
{
Write:($DATA(Arg1(i))>0) "Argument[", i , "]:",
?15, $GET(Arg1(i), "" ), !
}
Quit
}
因为方法是过程,所以它们支持 … 语法来接受可变数量的参数。
特殊种类的方法
使用 CodeMode 关键字可以定义其他特殊类型的方法:
调用方法是一种特殊机制,用于围绕现有 Caché 例程创建方法包装。调用方法的语法如下:
Method Call() [ CodeMode = call ]
{
Label^Routine
}
方法生成器是类编译器在类编译期间调用的程序。其输出是该方法的实际运行时实现。方法生成器提供了一种继承方法的方法,这些方法可以生成高性能的专用代码,这些代码根据继承类或继承属性的需要进行了自定义。在 Caché 库中,数据类型和存储类广泛使用方法生成器。
类查询
Caché 类可以包含类查询。类查询定义可由类使用的 SQL 查询,并指定要用作查询容器的类。下面显示了一个示例:
Query QueryName(Parameter As %String) As %SQLQuery
{
SELECT MyProperty, MyOtherProperty FROM MyClass
WHERE (MyProperty = "Hello" AND MyOtherProperty = :Parameter)
ORDER BY MyProperty
}
定义类查询以提供在应用程序中使用的预定义查找。例如,您可以按某些属性(如按名称)查找实例,或提供满足一组特定条件(如从巴黎到马德里的所有航班)的实例列表。此处显示的示例使用参数,这是提供灵活查询的常用方法。请注意,您可以在任何类中定义类查询;不需要在持久类中包含类查询
XData 块
因为 XML 通常是表示结构化数据的有用方法,所以 Caché
类包含一种机制,允许您包含格式正确的 XML 文档,以满足您可能有的任何需求。为此,请包括一个 XData 块
,这是另一种类型的类成员
。下面显示了一个示例:
XData Contents [XMLNamespace="http://www.intersystems.com/zen"]
{
<page xmlns="http://www.intersystems.com/zen" title="HelpDesk">
<html id="title">My Title</html>
<hgroup>
<pane paneName="menuPane"/>
<spacer width="20"/>
<vgroup width="100%" valign="top">
<pane paneName="tablePane"/>
<spacer height="20"/>
<pane paneName="detailPane"/>
</vgroup>
</hgroup>
</page>
}
Caché
将 XData
块用于某些特定目的,这些可能会为您提供有关您自己的应用程序的想法:
请注意,在所有这些情况下,XData 块必须包含在特定类型的类中。
类定义中的宏和包含文件
在Caché
类定义中,可以在方法中定义宏,并在该方法中使用它们。但是,更常见的情况是,您可以在包含文件中定义它们,您可以在任何类定义的开头包含该文件。例如:
Include (%assert, %callout, %occInclude, %occSAX)
/// Implements an interface to the XSLT Parser. XML contained in a file or binary stream
/// may be transformed
Class %XML.XSLT.Transformer Extends %RegisteredObject ...
然后,该类中的方法可以引用该包含文件或其包含文件中定义的任何宏。
宏是继承的。也就是说,子类可以访问与其超类相同的所有宏。
继承规则
与其他基于类的语言一样,您可以通过继承组合多个类定义。Caché
类定义可以扩展(或继承)多个其他类。反过来,这些类可以扩展其他类。
以下小节提供了 Caché
中类继承的基本规则。
Caché 使用以下规则进行继承顺序:
默认情况下,如果在多个超类中定义了给定名称的类成员,则该子类将从超类列表中最左侧的类中获取定义。
如果类定义包含继承 = right,则子类从超类列表中最右边的类中获取定义。
出于历史原因,大多数 Caché 类都包含 Inherit = right。
任何扩展其他类的类都有一个主超类。
无论类使用哪种继承顺序,主超类都是第一个,从左到右读取。
对于任何类级编译器关键字,给定类使用其主超类中指定的值。
对于持久类,主超类尤其重要;请参阅本书后面的“类和范围”。
尽管一个对象可以是属于多个类(如各种超类)范围的实例,但它始终具有最具体的类型类 (MSTC)。当对象是该对象的实例,但不是该类的任何子类的实例时,类是最具体的对象类型。
类从其一个或多个超类继承方法(类和实例方法),您可以重写这些超类。如果这样做,则必须确保方法定义中的签名与要重写的方法的签名匹配。这甚至包括,如果超类方法的匹配参数未指定数据类型,则子类方法的任何参数都不能指定数据类型。但是,子类中的方法可以指定超类中未定义的其他参数。
在子类中的方法中,可以引用它在超类中重写的方法。为此,请使用 ##super()
语法。例如:
//overrides method inherited from a superclass
Method MyMethod()
{
//execute MyMethod as implemented in the superclass
do ##super()
//do more things....
}
注意:##super 不区分大小写
。