如下展示Caché类定义,和一些典型元素
示例如下:
Class Demo.MyClass Extends %RegisteredObject
{
Property Property1 As %String;
Property Property2 As %Numeric;
Method MyMethod() As %String
{
set returnvalue=..Property1_..Property2
quit returnvalue
}
}
如下几个重点:
Demo.MyClass 涉及了一些Caché系统类。这个类是 %RegisteredObject(全名是 %Library.RegisteredObject) %String (%Library.String), 和 %Numeric (%Library.Numeric). %RegisteredObject 是Caché的关键类,因为它定义了对象接口,它提供了创建对象的实例, %String 和 %Numeric 数据类型类。因此,对应的属性有对应的值(不包含其他类型的值)。
Caché提供了大量类定义,可以从如下方式使用:
父类最常见的选择如下:
对于属性值、方法的参数值、方法返回的值等最常见的选择如下:
对象类指的是 %RegisteredObject 的子类,使用对象类,可以创建类的实例、指定实例的属性和调用实例的方法。
通用术语“对象”指的是对象类的实例。
对象类一般分为三类:
下图显示了这三个类之间的继承关系。方框中列出了类中定义的一些方法:
数据类型类指的是ClassType关键字等于数据类型或此类的任意子类。这些类不是对象类(数据类型类不能定义属性,也不能创建类的实例)。数据类型类(更准确地说是数据类型生成器类)的用途是用作对象类的属性类型
Caché类定义可以包括以下项目,都称为类成员:
映射机制由Java和c++映射使用;这就是映射一词的由来。
在形式上,有两种属性:属性和关系。
属性值Attribute和properties通常简称为属性。根据属性定义,它持有的值可以是以下任意一个:
通常,使用Studio来定义类。还可以使用Caché类定义类或通过XML类定义文件以编程方式定义类。如果使用SQL DDL语句定义一个SQL表,那么Caché将创建一个对应的类定义。
当定义一个类时,你可以选择一个或多个类作为父类,如果只有一个父类,那么在类定义时 Extends 关键字后面跟着父类
Class Demo.MyClass Extends Superclass
{
//...
}
如果有多个超类,请将它们指定为逗号分隔的列表,用圆括号括起来
Class Demo.MyClass Extends (Superclass1, Superclass2, Superclass3)
{
//...
}
创建类时不需要指定父类。使用%RegisteredObject作为父类是很常见的,即使该类不表示任何类型的对象,因为这样做使类可以访问许多常用的宏,可以直接使用它们的包含文件。
创建类不继承%RegisteredObject或其任何子类的类时,包含以下包含文件:
如果类继承了%RegisteredObject或它的任何子类,则可以自动使用这些宏。
还可以创建自己的包含文件,并根据需要将它们包含在类定义中。
若要在类定义的开头包含包含文件,请使用以下形式的语法。注意,必须忽略包含文件的.inc扩展名:
Include MyMacros
例如:
Include %occInclude
Class Classname
{
}
要在类定义的开头包含多个include文件,请使用以下形式的语法:
Include (MyMacros, YourMacros)
注意这个语法没有#号 。而且,Include指令不区分大小写,因此可以使用Include。包含文件名是大小写敏感的。
在某些情况下,有必要控制类编译器生成的代码的细节.例如,对于持久类,如果不想(或不能)使用默认表名,可以指定SQL表名.再如说,final 类不能创建它的子类,类定义支持一组特定的关键字。如果需要指定类关键字,请在父类后面的方括号中包含它们,如下所示:
Class Demo.MyClass Extends Demo.MySuperclass [ Keyword1, Keyword2, ...]
{
//...
}
例如,可用的类关键字包括Abstract和Final。
类参数为给定类的所有对象定义一个常数值。要在类定义中添加类参数,请在类中添加如下元素之一:
Parameter PARAMNAME as Type;
Parameter PARAMNAME as Type = value;
Parameter PARAMNAME as Type [ Keywords ] = value;
关键字表示任何参数关键字。
对象类可以包含属性。
要在类定义中添加属性,请在类中添加以下元素之一:
Property PropName as Classname;
Property PropName as Classname [ Keywords ] ;
Property PropName as Classname(PARAM1=value,PARAM2=value) [ Keywords ] ;
Property PropName as Classname(PARAM1=value,PARAM2=value) ;
PropName是属性的名称。
Classname是一个可选的类名(如果省略这个,则默认类型为%String)。
关键字表示任何属性关键字。
根据属性使用的类,还可以指定属性参数,如第三和第四种变体所示。
注意,如果包含属性参数,则将其括在圆括号中,并放在任何属性关键字之前。还要注意,如果包含属性关键字,则用方括号括起来。
可以在Caché类中定义两种方法:类方法和实例方法。
要在类定义中添加类方法,请在类中添加如下元素:
ClassMethod MethodName(arguments) as Classname [ Keywords]
{
//method implementation
}
Method MethodName(arguments) as Classname [ Keywords]
{
//method implementation
}
类和类成员遵循特定的命名约定。这些将在本节中详细介绍。
本节描述类名和成员名的规则,如最大长度、允许的字符等。完整的类名包括它的包名,如下一节所述。
每个标识符在其上下文中必须是唯一的(也就是说,没有两个类可以有相同的名称)。Caché对包名、类名和成员名有以下限制:
标识符保留大小写:必须与名称的大小写完全匹配;同时,两个类的名称不能仅大小写不同。例如,出于唯一性的目的,标识符“id1”和“ID1”被认为是相同的。
标识符必须以字母字符开头,尽管它们可能在第一个位置之后包含数字字符。标识符不能包含空格或标点符号,包名除外,包名可能包含"."。在Unicode系统中,标识符可能包含Unicode字符。
某些标识符以“%”字符开头;这标识了一个系统项。例如,Caché库提供的许多方法和包都以“%”字符开头。
可以对成员名进行分隔,这允许它们包含不允许的字符。若要创建带分隔符的成员名称,请对名称的第一个和最后一个字符使用双引号。例如:
Property "My Property" As %String;
每个类都有一个唯一标识它的名称。一个完整的类名由两部分组成:一个包名和类名,类名跟在最后一个点“.”后面。类名在其包内必须是唯一的;包名在Caché命名空间中必须是唯一的。
因为持久化类被自动映射为SQL表,类必须指定表名不能用SQL 保留字;如果持久性类的名称是一个SQL保留字,那么类定义还必须为其SQLTableName关键字指定一个有效的非保留字值。
每个类成员(如属性或方法)的名称必须在其类中是唯一的,并且最大长度为180个字符。此外,持久性的成员不能使用SQL保留字作为其标识符。但是,它可以使用该成员的SQLName或SQLFieldName关键字(视情况而定)定义别名。
注意:InterSystems强烈建议不要给两个成员相同的名称。这可能会产生意想不到的结果。
Caché类可以从已经存在的类继承。如果一个类从另一个类继承,继承类被称为子类,而它派生的一个或多个类被称为父类。
下面是一个使用两个父超类的类定义示例
Class User.MySubclass Extends (%Library.Persistent, %Library.Populate)
{
}
注意:这里显示的语法对应于Super关键字,它在Studio Inspector和导出为XML的类定义中可见。
除了从父类继承方法外,属性还从系统属性行为类继承其他方法,对于数据类型属性,则从数据类型类继承。
例如,如果有一个类定义为Person:
Class MyApp.Person Extends %Library.Persistent
{
Property Name As %String;
Property DOB As %Date;
}
从它派生一个新类Employee很简单:
Class MyApp.Employee Extends Person
{
Property Salary As %Integer;
Property Department As %String;
}
这个定义将Employee类建立为Person类的子类。除了自己的类参数、属性和方法之外,Employee类还包含Person类中的所有这些元素。
可以使用其超类的地方使用子类。例如,使用上面定义的Employee和Person类,可以打开Employee对象并将其引用为Person:
Set x = ##class(MyApp.Person).%OpenId(id)
Write x.Name
我们还可以访问特定于Employee类的属性或方法:
Write x.Salary //显示Salary属性(仅在雇员实例中可用)
子类继承的最左边的超类称为它的主父类。继承了它的父类的所有成员,包括适用的类关键字、属性、方法、查询、索引、类参数以及继承属性和继承方法的参数和关键字。除了标记为Final的项外,子类可以覆盖(但不能删除)其继承成员的特征。
通过多重继承,一个类可以从多个父类继承其行为和类类型。若要建立多重继承,请在括号内列出多个超类。最左边的超类是主父类。
例如,如果类X继承自类A、B和C,它的定义包括:
Class X Extends (A, B, C)
{
}
类编译器的默认继承顺序是从左到右的,这意味着父类之间成员定义的差异将根据最左边的超类来解决(在本例中,A将取代B和C, B将取代C)
因为从左到右的继承是默认的,所以不需要指定它;因此,前面的例子类定义等价于:
Class X Extends (A, B, C) [ Inheritance = left ]
{
}
要在父类之间指定从右到左的继承,请使用值为right的继承关键字:
Class X Extends (A, B, C) [ Inheritance = right ]
{
}
对于从右到左的继承,如果多个父类具有同名的成员,则右边的超类优先。
注意:即使使用从右到左的继承,最左边的父类(有时称为第一个父类)仍然是主父类。这意味着子类只继承它最左边的超类的class关键字值。
注意:在Caché版本2010.1之前,继承顺序总是从右向左的,不能改变。由于类字典升级,来自已升级的旧实例的类将自动继续使用从右到左的继承。因此,现有的代码不需要任何更改,即使新类默认从2010.1开始使用从左到右的继承。
如“Defining a Class: The Basics,”中所示,可以在类定义或类成员定义中包含关键字。这些关键字,也称为类属性,通常会影响编译器。本节介绍一些常见的关键字,并讨Caché存如何表示它们。
下面的例子展示了一个包含一些常用关键字的类定义:
/// 这个示例持久类表示一个person。
Class MyApp.Person Extends %Persistent [ SqlTableName = MyAppPerson ]
{
/// 为SSN属性定义唯一索引。
Index SSNKey On SSN [ Unique ];
/// Name of the person.
Property Name As %String [ Required ];
/// Person's Social Security number.
Property SSN As %String(PATTERN = "3N1""-""2N1""-""4N") [ Required ];
}
这个例子显示了以下关键字:
在许多情况下(但不是所有情况),当为类定义或类成员指定关键字时,将下列表单中的一个元素添加到类或类成员:
/// This sample persistent class represents a person.
/// Maintenance note: This class is used by some of the bindings samples.
Class Sample.Person Extends (%Persistent, %Populate, %XML.Adaptor)
{
...
对于这个类,Studio Inspector显示以下关键字表:
注意,名称和描述都是关键字。如果在检查器中编辑Description, Studio将更新类定义中的注释,反之亦然。类似地,有一个名为Super的关键字,它指定该类父类。如果编辑它,Studio将更新类定义的Extends部分。
当显示类成员时,Studio检查器有类似的行为。在这种情况下,Inspector窗口将显示一个表,其中包含所有成员关键字和当前选中成员的那些关键字的值。(对于属性,Inspector窗口还列出可用的属性参数及其当前值。)
将类定义导出到XML时,导出的文件如下:
Maintenance note: This class is used by some of the bindings samples.]]>
%Persistent,%Populate,%XML.Adaptor
63540,49568.139638
59269,38836.623
Name,SSN,Home.City,Home.State
...
该文件中的大多数XML元素都对应于编译器关键字。
当以编程方式访问类定义时,类定义实例包含与关键字对应的属性。
Caché提供了一个称为InterSystems类引用的web页面,该页面显示由InterSystems提供的类以及创建的类自动生成的引用信息。通俗地说,类引用被称为documentation,因为它是由类%CSP.Documatic.生成的。
类引用的目的是向其他程序告知类的哪些部分可以使用,以及如何使用它们。以下是一个例子:
此参考信息显示类成员的定义,但不显示它们的实际实现。例如,它显示方法签名,但不显示其内部定义。它包括元素之间的链接,以便可以快速地遵循代码的逻辑;在某些情况下,这比使用Studio要快。还有一个搜索选项。
要创建包含在类引用中的文档,请在类定义中创建注释—特别是以///开头的注释。如果在类声明之前加上这样的注释,则该类的注释将显示在页面的顶部。如果在给定的类成员之前加上这样的注释,则注释将显示在为该类成员生成的信息之后。
例如,在SAMPLES名称空间中,是Sample。Person类包括以下属性定义:
/// Person's Date of Birth.
Property DOB As %Date(POPSPEC = "Date()");
/// Person's age.
/// This is a calculated field whose value is derived from DOB .
Property Age As %Integer [ Calculated, SqlComputeCode = { Set {Age}=##class(Sample.Person).CurrentAge({DOB})
}, SqlComputed, SqlComputeOnChange = DOB ];
将其与前面显示的类引用的图片进行比较。
默认情况下,表示将组合所有///行的文本,并将结果作为单个段落处理。可以插入HTML换行符。
注释的长度必须小于系统的最大字符串长度。
可以在类中的注释中使用HTML标记。除了标准的HTML之外,还可以使用以下标记:类、方法、属性、参数、查询和示例。(与标准HTML标记一样,这些标记的名称不区分大小写。)这里描述了最常用的标记。
用于标记类名。如果类存在,则内容将显示为类文档的链接。例如:
/// This uses the Sample.Person class.
用于标记编程示例。此标记影响文本的外观。请注意,在本例中,每一行都变成了单独的一行(与通常的情况不同,在这种情况下,行被合并成一个段落)。例如:
///
/// set o=..%New()
/// set o.MyProperty=42
/// set o.OtherProp="abc"
/// do o.WriteSummary()
///
用于标记方法名。如果方法存在,内容将显示为方法文档的链接。例如:
/// This is identical to the Unique method.
用于标记属性名。如果属性存在,则内容将显示为指向属性文档的链接。例如:
/// This uses the value of the State property.
下面是使用HTML标记的多行描述
/// The Factorial method returns the factorial
/// of the value specified by x.
Caché存类定义由Caché类编译器编译到应用程序例程中。类在编译之前不能在应用程序中使用。
Caché类编译器与其他编程语言(如c++或Java)中可用的编译器有两个重要的区别:首先,编译结果放在共享存储库(数据库)中,而不是文件系统中。其次,它自动提供对持久性类的支持。
具体来说,类编译器做以下工作:
如果使用类编译器指定了Keep Generated Source选项,可以在Studio中使用view Other Code命令(从view菜单中)查看例程的源代码。
6. 将所有生成的程序编译成可执行代码。
7. 创建了一个类描述符。这是一个特殊的数据结构(存储做为程序),包含支持类所需的所有运行时调度信息(属性名、方法位置等)。
有几种方法来调用类编译器:
Do $System.OBJ.Compile("MyApp.MyClass")
如果使用SQL DDL(Data Definition Languages)语句创建表,则会自动调用类编译器来编译与表对应的持久类。
在编译类时,如果正在编译的类包含有关依赖项的信息,那么Caché还将重新编译其他类。例如,Caché编译类的任意子类。在某些情况下,可能需要控制类编译的顺序。这样做,使用System,DependsOn和CompileAfter关键字。
要查找编译器将在编译给定类时重新编译的类, 使用$SYSTEM.OBJ.GetDependencies()方法。例如:
SAMPLES>d $system.OBJ.GetDependencies("Sample.Address",.included)
SAMPLES>zw included
included("SOAP.Demo.LookupCity")=""
included("SOAP.DemoProxy.LookupCity")=""
included("Sample.Address")=""
included("Sample.Customer")=""
included("Sample.Employee")=""
included("Sample.Person")=""
included("Sample.Vendor")=""
该方法的签名如下:
classmethod GetDependencies(ByRef class As %String, Output included As %String, qspec As %String) as %Status
Compile()方法还允许提供影响结果的标志和限定符。它们在参数列表中的位置在Compile()方法的解释中进行了描述。要查看适用的标志,请执行以下命令:
Do $System.OBJ.ShowFlags()
要查看限定符的完整列表,请执行以下命令:
Do $System.OBJ.ShowQualifiers()
在编译包含位图索引的类时,如果没有为该类定义位图区段索引,则类编译器将生成位图区段索引。在向生产系统上的类添加位图索引时需要特别注意。
如果编译器在正在编译的类的实例打开时被调用,则不存在错误。已经打开的实例继续使用其现有代码。如果另一个实例在编译后打开,它将使用新编译的代码。
可能希望在将一些类发送给客户之前对它们进行部署;这个过程隐藏源代码。
对于包含不希望客户看到的方法定义的任何类定义,编译这些类,然后使用$SYSTEM.OBJ.MakeClassDeployed().。例如:
d $system.OBJ.MakeClassDeployed("MyApp.MyClass")
当类处于部署模式时,其方法和触发器定义已被删除。
可以在Studio中打开类定义,但它是只读的。
不能导出或编译已部署的类,但可以编译它的子类(如果没有部署的话)。
无法逆转或撤消类的部署。但是,如果以前导出过类,那么可以通过从文件中导入定义来替换该类。(如果不小心过早地将某个类放入部署模式,这将非常有用。)