第五章 定义和调用方法
方法介绍
方法是由类定义的可执行元素。Caché 支持两种类型的方法:实例方法和类方法。从类的特定实例调用实例方法,并通常执行与该实例相关的一些操作。类方法是无需引用任何对象实例即可调用的方法;这些在其他语言中称为静态方法。
方法通常指的是实例方法。更具体的类方法用于引用类方法。
因为没有对象的实例就不能执行实例方法,所以实例方法只有在对象类中定义时才有用。相反,可以在任何类中定义类方法。
定义方法
要将类方法添加到类中,请在类定义中添加如下元素:
ClassMethod MethodName(Arguments) as Classname [ Keywords]
{
//method implementation
}
- MethodName 是方法的名称,最长180个字符。
- Arguments 参数以逗号分割。
- Classname 是一个可选的类名,表示此方法返回的值的类型。如果方法不返回值,则省略As Classname部分。
类可以是数据类型类、对象类,或者(不太常见的)无类型类。类名可以是完整的类名,也可以是简短的类名。(如果用IMPORT 关键字可以使用简称)
- Keywords 代表关键字
- 方法的实现取决于实现语言和方法的类型;
要将实例方法添加到类中,请使用与方法相同的语法,而不是使用ClassMethod:
Method MethodName(arguments) as Classname [ Keywords]
{
//method implementation
}
实例方法只与对象类相关。
指定方法参数:基础
方法可以接受任意数量的参数。方法定义必须指定它所接受的参数。还可以为每个参数指定类型和默认值。(在这个上下文中,type指的是任何类型的类,而不是特定的数据类型类。)
通用类方法定义:
ClassMethod MethodName(Arguments) as Classname [ Keywords]
{
//method implementation
}
这里参数可以有以下形式
argname1 as type1 = value1, argname2 as type2 = value2, argname3 as type3 = value3, [and so on]
- argname1, argname2, argname3, and so on 是参数名,这些名称必须遵循变量名称的规则。
- type1, type2, type3, and so on 是类名 方法定义的这一部分旨在向可能使用此方法的程序员描述要传递什么类型的值作为相应的参数。通常,显式地指定每个方法参数的类型是一个好习惯。
通常,类型是数据类型类或对象类。
类名可以是完整的类名,也可以是简短的类名。(IMPORT关键字导入类可使用简名)
可以忽略这部分语法。如果你这样做了,也可以省略as部分。
- value1, value2, value3, and so on 是参数值,如果在没有为参数提供值的情况下调用方法,则该方法将自动将参数设置为该值。
每个值可以是一个字符串(“abc”或42),也可以是一个用大括号括起来的Caché ObjectScript表达式。例如:
ClassMethod Test(flag As %Integer = 0)
{
//method implementation
}
或
ClassMethod Test(time As %Integer = {$horolog} )
{
//method implementation
}
可以忽略这部分语法。如果这样做了,也可以省略等号(=)。
例如,下面是一个带有三个参数的Calculate()方法:
ClassMethod Calculate(count As %Integer, name, state As %String = "CA")
{
// ...
}
其中count和state分别声明为%Integer和%String。默认情况下,参数是%String数据类型,因此未指定类型的参数是%String。以上示例中的name就是这种情况。
如何传递参数
方法定义还表明,对于可能使用该方法的程序员,如何传递每个参数。参数可以通过值或引用传递(ref)
通过引用传递特定的参数可能明智,也可能不明智。细节取决于方法实现。因此,在定义方法时,应该使用方法签名向其他程序员表明如何使用每个参数。
要指示应通过引用传递参数,请在方法签名中参数名称之前包含ByRef修饰符。
下面的示例使用了ByRef的两个参数:
/// Swap value of two integers
Method Swap(ByRef x As %Integer, ByRef y As %Integer)
{
Set temp = x
Set x = y
Set y = temp
}
类似地,为了指示参数应该通过引用传递,并且不希望传入值,在方法签名中在参数名之前包含Output修饰符。
例如:
Method CreateObject(Output newobj As MyApp.MyClass) As %Status
{
Set newobj = ##class(MyApp.MyClass).%New()
Quit $$$OK
}
可变数量的参数
可以定义一个接受可变数量参数的方法。为此,包括…在最后一个参数的名称之后,如下面的示例所示。这个例子还展示了如何使用这个特性。
ClassMethod MultiArg(Arg1... As %String)
{
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), "<NULL>"), !
}
Quit
}
下面的终端会话展示了这个方法的行为:
SAMPLES>do ##class(VarNumArg.Demo).MultiArg("scooby","shaggy","velma","daphne","fred")
Invocation has 5 elements
Argument[1]: scooby
Argument[2]: shaggy
Argument[3]: velma
Argument[4]: daphne
Argument[5]: fred
注意:$GET(Arg1, 0) 如果为空,第二个参数是代表默认值
返回值
要定义一个方法以便它返回一个值,请在该方法中使用下列任一项(如果在Caché ObjectScript中实现该方法):
Return returnvalue
或
Quit returnvalue
其中returnvalue是方法返回的合适值。这应该与方法声明的返回类型一致。如果返回类型是数据类型类,则该方法应该返回一个文字值。如果返回类型是一个对象类,那么该方法应该返回该类的一个实例(特别是OREF)。
例如
ClassMethod Square(input As %Numeric) As %Numeric
{
Set returnvalue = input * input
Return returnvalue
}
返回对象
/// w ##class(PHA.OP.MOB.Test).FindPerson(2)
ClassMethod FindPerson(id As %String) As PHA.OP.MOB.Android
{
Set person = ##class(User.DHCPHARWIN).%OpenId(id)
Return person
}
DHC-APP>w ##class(PHA.OP.MOB.Test).FindPerson(2)
DHC-APP 2e1>w person
1@User.DHCPHARWIN
DHC-APP 2e1>zw person
person=<OBJECT REFERENCE>[1@User.DHCPHARWIN]
+----------------- general information ---------------
| oref value: 1
| class name: User.DHCPHARWIN
| %%OID: $lb("2","User.DHCPHARWIN")
| reference count: 2
+----------------- attribute values ------------------
| %Concurrency = 1 <Set>
| PHAAutoPrintDate = ""
| PHAAutoPrintIP = ""
| PHAAutoPrintTime = ""
| PHAChkStatus = ""
| PHAChkUser = ""
| PHAChkUserCode = ""
| PHAConfigUser = ""
| PHAConfigUserCode = ""
| PHADATE = 65261
| PHADeviceID = ""
| PHAEMFLAG = ""
| PHAFINDATE = 64730
| PHAFINFLAG = ""
| PHALastDate = 65261
| PHALastTime = 37684
| PHALocCode = ""
| PHANOUSER = ""
| PHAPRINTDATE = 65259
| PHAPRINTFLAG = ""
| PHAPRINTTIME = 37686
| PHAPRTDR = 223382
| PHAPresChkDate = ""
| PHAPresChkTime = ""
| PHAPriorFlag = "N"
| PHARETFLAG = 1
| PHAStartPyFlag = 10
| PHAStatus = 10
| PHATIME = 53860
| PHAWinDesc = ""
| PHAZFCL = ""
| PhaPrescNo = "O180323000005"
| PhaUndsipFlag = ""
| phalcdserialno = 0
+----------------- swizzled references ---------------
| i%PHAPAPMIDR = 81
| r%PHAPAPMIDR = ""
| i%PHAPHLDR = 11
| r%PHAPHLDR = ""
| i%PHAPHWDR = 42
| r%PHAPHWDR = ""
| i%PHAPRINTUSERDR = ""
| r%PHAPRINTUSERDR = ""
| i%PHAQueueNo = ""
| r%PHAQueueNo = ""
| i%Phaphpydr = ""
| r%Phaphpydr = ""
+-----------------------------------------------------
注意:Return与Quit用法相同
实现语言
在创建方法时,可以选择实现语言。实际上,在一个类中,可以用不同的语言实现多个方法。所有方法互操作,而不考虑实现语言。
默认情况下,方法使用其所属类的language关键字指定的语言。对于这个关键字,默认是Caché (Caché ObjectScript)。其他选项有basic (Cache basic)、java (java)、javascript (javascript)、mvbasic (mvbasic)和tsql (tsql)。
注意:Caché ObjectScript Caché脚本 即M语言
可以通过为特定的方法设置语言关键字来覆盖它:
Class MyApp.Test {
/// A Basic method
Method TestB() As %Integer [ Language = basic]
{
'This is Basic
Print "This is a test"
Return 1
}
/// A Cache ObjectScript method
Method TestC() As %Integer [ Language = Caché]
{
// This is Cache ObjectScript
Write "This is a test"
Quit 1
}
}
方法的类型(CodeMode选项)
Caché 支持四种类型的方法,类编译器处理不同:
代码方法 Code Methods
代码方法的实现只是几行代码。这是最典型的方法类型,也是默认的。
方法实现可以包含对实现语言有效的任何代码。
注意:Caché提供了一组系统定义的方法来执行简单、常见的任务。如果用户定义的方法执行这些任务之一,则编译器不会为其生成任何可执行代码。相反,它将用户定义的方法与系统定义的方法关联起来,因此调用用户定义的方法将导致对系统定义的方法的调用,从而带来相关的性能好处。而且,调试器不会单步执行这样一个系统定义的方法。
表达方法 Expression Methods
表达式方法是一种方法,在某些情况下,类编译器可以使用指定表达式的直接内联替换来替换它。表达式方法通常用于需要快速执行速度的简单方法(如数据类型类中的方法)。
例如,可以将Dog类的Speak()方法从前面的示例转换为表达式方法:
/// w ##class(PHA.OP.MOB.Test).Speak()
ClassMethod Speak() As %String [ CodeMode = expression ]
{
"Woof, Woof"
}
DHC-APP>w ##class(PHA.OP.MOB.Test).Speak()
Woof, Woof
假设dog是指dog对象,则该方法可采用如下方法:
Write dog.Speak()
这可能导致生成以下代码:
Write "Woof, Woof"
为表达式方法的所有形式变量提供默认值是一个好主意。这可以防止由于运行时缺少实际变量而导致的潜在内联替换问题。
注意:Caché不允许在表达式方法中使用宏或引用调用参数。
调用方法 Call Methods
调用方法是在现有Caché 程序周围创建方法包装器的一种特殊机制。在将遗留代码迁移到基于对象的应用程序时,这通常很有用。
Method Call() [ CodeMode = call ]
{
Tag^Routine
}
其中“Tag^例程”指定了程序例程中的一个标签名。
注意:CodeMode = call没有这个关键字 程序报错
Call()
s dog="小狗"
w dog,!
q dog
/// w ##class(PHA.OP.MOB.Test).Call()
ClassMethod Call() [ CodeMode = call ]
{
Call^PHA.MOB.TEST
}
DHC-APP>w ##class(PHA.OP.MOB.Test).Call()
小狗
注意:routine是宏程序 Tag^Routine
方法生成器 Method Generators
方法生成器实际上是一个由类编译器在类编译期间调用的小程序。它的输出是该方法的实际运行时实现。方法生成器提供了一种继承方法的方法,可以生成高性能的专门代码,这些代码根据继承类或属性的需要进行定制。在Caché库中,数据类型和存储类广泛使用方法生成器。
/// d ##class(PHA.OP.MOB.Test).MyMethod()
/// PHA.OP.MOB.Test
ClassMethod MyMethod() [ CodeMode = objectgenerator ]
{
Do %code.WriteLine(" Write """ _ %class.Name _ """")
Do %code.WriteLine(" Quit")
Quit $$$OK
}
DHC-APP 2d1> d ##class(PHA.OP.MOB.Test).MyMethod()
PHA.OP.MOB.Test
注意:必须有objectgenerator关键字 才可以用%class,%code 关键字
将方法映射为SQL存储过程
可以定义一个类方法(但不是实例方法),以便它也可以作为SQL存储过程使用。为此,在方法定义中包含SqlProc关键字。
该过程的默认名称为CLASSNAME_METHODNAME。要指定一个不同的名称,请指定SqlName关键字。
调用类方法
本节讨论如何在Caché ObjectScript中调用类方法。本节适用于所有的类。注意,下一章将讨论实例方法,因为它们只适用于对象类。
- 要调用任何类的类方法(如果该方法不是私有的),请使用以下表达式:
##class(Package.Class).Method(Args)
- Package.Class 是类名
- Method 方法名
- Args 方法的参数
-
class 不区分大小写
这个表达式调用给定的类方法并获得它的返回值(如果有的话)。可以将此表达式与DO和SET等命令一起使用,也可以将其用作另一个表达式的一部分。以下是一些变化:
do ##class(Package.Class).Method(Args)
set myval= ##class(Package.Class).Method(Args)
write ##class(Package.Class).Method(Args)
set newval=##class(Package.Class).Method(Args)_##class(Package2.Class2).Method2(Args)
可以忽略这个包。如果这样做,类编译器将确定要使用的正确包名 (IMPORT)
- (在一个类方法中)调用该类的另一个类方法(可以是一个继承的方法),使用以下表达式:
..MethodName(args)
可以使用DO命令来使用这个表达式。如果方法返回一个值,可以使用SET,或者将其用作另一个表达式的一部分。以下是一些变化:
do ..MethodName()
set value=..MethodName(args)
注意:不能在类方法中使用此语法来引用属性或实例方法,因为这些引用需要实例上下文。
- 要执行一个类方法,其中的方法名直到运行时才确定,使用$CLASSMETHOD函数:
$CLASSMETHOD(classname, methodname, Arg1, Arg2, Arg3, ... )
- classname 计算为类的全名。
- methodname 计算结果为该类中的类方法的名称,
- Arg1, Arg2, Arg3 方法参数
/// d ##class(PHA.OP.MOB.Test).CLASSMETHODTEST()
/// 输出 Woof, Woof
ClassMethod CLASSMETHODTEST()
{
set cls="PHA.OP.MOB.Test"
set clsmeth="Speak"
S RET= $CLASSMETHOD(cls,clsmeth)
q RET
}
如果给定的方法不存在,或者它是一个实例方法,Caché生成的<方法不存在><METHOD DOES NOT EXIST>错误。如果给定的方法是私有的,缓存将生成<私有方法> <PRIVATE METHOD>错误。
- PRIVATE 方法 ##super可以调用 其他类则报错
DHC-APP>w ##class(PHA.OP.MOB.API).Login("demo#1")
{"retVal":"success","retObject":{"userCode":"demo","userName":"Demo Group","userID":1,"rows":[{"locID":"312","locDesc":"煎药室","groupID":"176","groupDesc":"煎 药室"}]}}
DHC-APP>w ##class(PHA.OP.MOB.Business).Login("yf01#1")
W ##CLASS(PHA.OP.MOB.Business).Login("yf01#1")
^
<PRIVATE METHOD>
将参数传递给方法
将参数传递给方法的默认方式是按值传递。在这种技术中,只需将参数作为变量、文字值或其他表达式包含在方法调用中,如前面的示例所示。
也可以通过引用传递参数。
它的工作原理如下:系统有一个包含每个局部变量值的内存位置。变量的名称充当内存位置的地址。将局部变量传递给方法时,将通过值传递变量。这意味着系统复制了该值,因此原始值不会受到影响。你可以传递内存地址;这种技术称为引用调用。它也是传递多维数组作为参数的唯一方法。
在Caché ObjectScript中,若要通过引用传递参数,请在该参数之前加上"."。例如:
set MyArg(1)="value A"
set MyArg(2)="value B"
set status=##class(MyPackage.MyClass).MyMethod(.MyArg)
在本例中,通过引用传递一个值(一个多维数组),以便该方法可以接收该值。在其他情况下,通过引用传递参数是很有用的,这样就可以在运行方法之后使用它的值。例如:
set status=##class(MyPackage.MyClass).GetList(.list)
//use the list variable in subsequent logic
在其他情况下,可以为变量指定一个值,调用修改它的方法(并通过引用返回它),然后使用更改后的值。
/// d ##class(PHA.OP.MOB.Test).TestPassingArguments()
ClassMethod TestPassingArguments()
{
set MyArg(1)="value A"
set MyArg(2)="value B"
S RET=..TestPassingArguments1(.MyArg)
zw MyArg
q RET
}
ClassMethod TestPassingArguments1(ByRef MyArg)
{
set MyArg(3)="value C"
set MyArg(4)="value D"
b
q "TestPassingArguments1"
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestPassingArguments()
b
^
<BREAK>zTestPassingArguments1+3^PHA.OP.MOB.Test.1
DHC-APP 3e1>zw MyArg
MyArg(1)="value A"
MyArg(2)="value B"
MyArg(3)="value C"
MyArg(4)="value D"
DHC-APP 3e1>g
MyArg(1)="value A"
MyArg(2)="value B"
MyArg(3)="value C"
MyArg(4)="value D"
DHC-APP>
铸造方法
要将一个类的方法转换为另一个类的方法,语法如下(在cache ObjectScript中):
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,,这个方法如下
Method Go()
{
Write "Go down.",!
}
然后可以创建MyClass.Up的一个实例。并使用它来调用MyClass.Down。方法:
>Set LocalInstance = ##class(MyClass.Up).%New()
>Do ##class(MyClass.Down)LocalInstance.Go()
Go down.
在表达式中使用##类也是有效的
Write ##class(Class).Method(args)*2
没有设置一个等于返回值的变量。
更通用的方法是使用CLASSMETHOD函数,它们分别是实例方法和类方法。这些在本章前面的章节中有描述。
Class PHA.OP.MOB.TestTwo Extends PHA.OP.MOB.Test
{
Method Go()
{
Write "Go down.",!
}
}
Class PHA.OP.MOB.Test Extends %RegisteredObject
{
Method Go()
{
Write "Go up.",!
}
/// d ##class(PHA.OP.MOB.Test).CastingMethod()
ClassMethod CastingMethod()
{
Set LocalInstance = ##class(PHA.OP.MOB.TestTwo).%New()
Do ##class(PHA.OP.MOB.Test)LocalInstance.Go()
}
}
DHC-APP>d ##class(PHA.OP.MOB.TestThree).CastingMethod()
Do ##class(PHA.OP.MOB.Test)LocalInstance.Go()
^
<INVALID CLASS>zCastingMethod+2^PHA.OP.MOB.TestThree.1 *super-class 'PHA.OP.MOB.Test' is not in sub-class 'PHA.OP.MOB.TestTwo' hierarchy
DHC-APP 2d1>k
DHC-APP 2d1>q
DHC-APP>d ##cla
注意必须是主子关系的类!
重写继承的方法
类从父类或父类继承方法(类和实例方法)。除了标记为Final的方法外,可以通过在该类中提供定义来覆盖这些定义。
/// d ##class(PHA.OP.MOB.Test).CLASSMETHODTEST()
/// 输出 Woof, Woof
ClassMethod CLASSMETHODTEST() [ Final ]
{
set cls="PHA.OP.MOB.Test"
set clsmeth="Speak"
S RET= $CLASSMETHOD(cls,clsmeth)
q RET
}
子类重写父类FINAL方法时会报错
Studio
---------------------------
错误 #5272: 无法更改最终'Method': 'CLASSMETHODTEST'
> 错误 #5030: 在编译类PHA.OP.MOB.TestTwo时出错
---------------------------
确定
---------------------------
如果这样做,请注意以下规则:
- 如果方法是父类中的类方法,则不能将其作为子类中的实例方法覆盖,反之亦然。
- 子类方法的返回类型必须与原始返回类型或原始返回类型的子类相同。
- 子类中的方法可以比父类中的方法有更多的参数。
- 子类中的方法可以为参数指定不同的默认值。
- 子类方法中的参数类型必须与原始方法中的参数类型一致。具体地说,任何给定的参数必须与原始类型或原始类型的子类相同。
注意,如果一个参数没有指定类型,编译器会将该参数视为%String。因此,如果超类方法中的参数没有类型,则子类方法的相应参数可以是%String,可以是%String的子类,也可以没有类型。
- 子类中的方法应该以与父类中的方法相同的方式接收参数值。例如,如果给定的参数是通过引用在父类中传递的,那么同样的参数应该通过引用在子类中传递。
如果方法签名在这方面不一致,其他开发人员就很难知道如何正确使用方法。但是,请注意,编译器不会发出错误。
如果的方法实现需要调用与父类中定义的同名方法,可以使用语法##super(),这将在小节中讨论。
##super()关键字
在一个方法中,使用下面的表达式来调用在最近的父类中定义的同名方法:
##super()
可以用DO命令来使用这个表达式。如果方法返回一个值,可以使用SET,或者将其用作另一个表达式的一部分。以下是一些变化:
do ##super()
set returnvalue=##super()_"additional string"
注意:##super不区分大小写。还要注意,与本章中的其他特性不同,##super()可以在Basic 本方法中使用,也可以在ObjectScript方法中使用。
如果定义了一个方法,该方法应该调用超类的现有方法,然后执行一些附加的步骤,例如修改它的返回值,那么这是非常有用的。
##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.Down).SelfAdd(2)
2
4
6
>
在MyClass.Down.SelfAdd()中,注意参数名前面的句点"."。如果我们忽略".",并且在不提供参数的情况下调用方法,我们将收到一个 <UNDEFINED> error。
DHC-APP> d ##class(PHA.OP.MOB.TestTwo).SelfAdd(3)
Write Arg,!
^
<UNDEFINED>zSelfAdd+1^PHA.OP.MOB.Test.1 *Arg
注意 父类如果有参数,必须指定参数 否则报错。
调用 ##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),确保父类中的方法为添加的参数指定默认值。
ClassMethod SelfAdd(Arg As %Integer, ARG2 As %Integer = 2)
{
Write Arg,!
Write Arg + Arg+ARG2
}
/// d ##class(PHA.OP.MOB.TestTwo).SelfAdd(1)
ClassMethod SelfAdd(Arg1 As %Integer)
{
Do ##super(Arg1)
Write !
b
Write Arg1 + Arg1 + Arg1
}
注意:父类如果有修改参数,一定要添加默认值