Show/Hide Toolbars

XSharp

用途

声明一个方法名和一个可选的局部变量名列表。

语法

 [Attributes] [Modifiers] METHOD <idMethod>

 [Typeparameters]

 [([<idParam> [AS | REF|OUT|IN <idType>] [, ...])]

 [AS <idType>]

 [TypeparameterConstraints]

 [<idConvention>]
 [CLASS <idClass>]

 [=> <expression>]
 CRLF

 [<Body>]

 [END METHOD]

参数

Attributes一个可选的特性列表,用于描述实体的元信息,例如在 MsTest 类库中包含测试的方法/函数上的 [TestMethod] 属性。请注意,当特性写在关键字上方的行时,特性必须在同一行或以分号结尾。

 

Modifiers一个可选的修饰符列表,用于指定实体的可见性或范围,例如:PUBLIC, PROTECTED, HIDDEN, INTERNAL, SEALED, ABSTRACT 或 STATIC.

 

<idMethod>方法的有效标识符名称。 方法名称在类中必须是唯一的,但可以与应用程序中的其他实体(包括访问和赋值方法)共享相同的名称。

 

TypeParameters这适用于具有泛型类型参数的方法。这类似于 `<T>`,用于具有一个名为 T 的类型参数的方法。通常,参数列表中的一个参数也是类型 T。
<idParam>参数变量。 以这种方式指定的变量会自动声明为局部变量。 这些变量也称为形参(形式参数),用于接收调用实体时传递的参数。

 

AS | REF | OUT | IN <idType>指定参数变量的数据类型(称为强类型)。 AS 表示参数必须通过值传递,REF 表示参数必须通过带 @ 操作符的引用传递。OUT 是一种特殊的 REF 参数,不必在调用前赋值,必须在实体内部赋值。IN 参数作为 READONLY 引用传递。
列表中的最后一个参数也可以声明为 PARAMS <idType>[] ,这将告诉编译器函数/方法可以接收零个或多个可选参数。
使用 CLIPPER 调用约定的函数或方法将被编译为一个带有单个参数的函数,该参数被声明为 Args PARAMS USUAL[] 。
AS <idType>指定数据类型。如果省略,编译器依据编译选项来决定数据类型,或者是 USUAL,或者是由编译器自行决定。

 

TypeParameterConstraints在这里,您可以为类型参数指定约束,例如 WHERE T IS SomeName 或 WHERE T IS New。
<idConvention>指定此实体的调用约定。 <idConvention> 必须是以下内容之一:

o        CLIPPER

o        STRICT

o        PASCAL

o        CALLBACK

o        THISCALL

大多数 调用约定 仅用于向后兼容。
但是,存在两个例外情况:
1. CLIPPER 声明方法具有未类型化参数。通常只有没有声明参数的方法才需要这样做。否则,编译器在检测到未类型化参数时将假定使用 CLIPPER 调用约定。
2. 外部 DLL 的方法和函数可以使用 STRICT、PASCAL、 CALLBACK 调用约定。

 

 

CLASS <idClass>该方法所属的类。对于在 CLASS ... END CLASS 结构之外声明的实体,必须使用该子句。

 

=> <Expression>替代实体多行正文(body)的单一表达式。不能与正文(body)一起编译

 

<Body>构成此主体代码的程序语句。
<Body> 可以包含一个或多个 RETURN 语句,以将控制权返回给调用例程,并作为函数的返回值。如果未指定 RETURN 语句,当函数定义结束时,控制权将返回给调用例程,并且函数将根据指定的返回值数据类型返回一个默认值(如果返回值没有强类型,则返回 NIL)。
不能与表达式主体(Expression Body)结合使用。
END METHOD可选的结束语句,用于表示内联 METHOD 主体的结束

描述

方法是一个子程序,由一系列声明和语句组成,每当使用信息发送操作符引用方法时,都会执行这些声明和语句:

<idObject>:<idMethod>([<uArgList>])

SELF:<idMethod>([<uArgList>])

类、实例变量(参见本指南中的 CLASS 语句)和方法是面向对象编程的基本单元。 您将在应用程序中使用方法来组织特定对象类的计算代码块。

备注

Start() 方法:  所有应用程序都必须有一个名为 Start() 的函数或过程。

Start() 是应用程序执行时的启动例程。 Start()必须声明为不带参数或带字符串数组参数,并且必须具有 Int 返回值或 Void 类型。

 

VO 兼容性:

 

VO 有两个特殊的方法名称,用于构造和销毁类对象: Init() 和 Axit()。

在 X# 中,这些方法应被命名为 CONSTRUCTORDESTRUCTOR

如果使用编译器选项 /vo1 进行编译,则仍可使用 "旧 "名称。编译器会自动将 Init() 方法映射为构造函数,将 Axit() 方法映射为析构函数。不建议这样做。

 

Init() 方法:如果定义了名为 Init() 的方法,那么在创建该方法所属类的实例时,就会自动调用该方法。 实例化操作符({})中列出的参数将作为参数传递给 Init() 方法。 Init() 方法的常见用途包括初始化实例变量、分配对象所需的内存、注册对象、创建附属对象以及建立对象之间的关系。

 

Axit() 方法:  由于垃圾回收器会自动处理对象使用的内存,因此无需重新分配内存。 不过,在某些情况下,对象可以管理其他需要妥善处置的资源。 例如,如果一个对象只为自己的使用而打开一个数据库,那么它就应该在使用完毕后关闭该数据库,并将工作区用于其他用途。

 

如果使用 RegisterAxit() 函数(例如在 Init() 方法中)注册一个对象,并提供一个名为 Axit() 的方法,垃圾回收器就会在对象销毁前自动调用该 Axit() 方法。 因此,您可以在 Axit() 方法中关闭数据库、去分配内存或关闭通信链路。

 

NoIVarGet()/NoIVarPut() 方法:  如果定义了名为 NoIVarGet() 和 NoIVarPut() 的方法,在引用不存在的实例变量时,它们将被自动调用。 调用时会将实例变量的名称作为参数,以符号的形式调用,而在 NoIVarPut() 的情况下,还会调用赋值。 这一功能对于检测和防止运行时错误以及在运行时动态创建虚拟变量非常有用。 例如,DBServer 类使用这种技术使数据库字段看起来像是数据库对象的导出实例变量:

METHOD NoIVarGet(symFieldName) CLASS DBServer
  BEGIN SEQUENCE
    RETURN FieldGetAlias(symAlias, symFieldName)
  RECOVER
    // Pass it up if it was not a field name
    SUPER:NoIVarGet(symFieldName)
  END SEQUENCE
 
METHOD NoIVarPut(symFieldName, uValue) ;
  CLASS DBServer
  BEGIN SEQUENCE
    RETURN FieldGetAlias(symAlias, ;
        symFieldName, uValue)
  RECOVER
    // Pass it up if it was not a field name
    SUPER:NoIVarPut(symFieldName, uValue)
  END SEQUENCE
 
FUNCTION DatabaseTest()
  LOCAL oDBServer AS DBServer
  oDBServer := DBServer{"customer"}
  ? oDBCustomer:CustName
  oDBCustomer:ZipCode := "12345"

NoMethod() 方法:  如果你定义了一个名为 NoMethod() 的方法,当你在同一类中调用的方法名称找不到时,它将自动被调用。 传递的参数将与原始方法相同。 这一功能有助于检测和防止在找不到方法时出现运行时错误。 使用 NoMethod() 函数可找出无法找到的方法名称。

 

通过代码块导出局部变量:  创建代码块时,可以在代码块定义中访问创建实体中定义的局部变量,而无需将其作为参数传递(即局部变量对代码块是可见的)。 利用这一事实以及可以将代码块作为参数传递的事实,就可以导出局部变量。 例如

METHOD One() CLASS MyClass EXPORT LOCAL
 LOCAL nVar := 10 AS INT, cbAdd AS CODEBLOCK
 cbAdd := {|nValue| nValue + nVar}
 
 ? SELF:Two(cbAdd)                // Result:  210
 
METHOD Two(cbAddEmUp) CLASS MyClass
 RETURN EVAL(cbAddEmUp,200)

当代码块在 Two() 中求值时,方法 One() 本地的 nVar 变为可见,尽管它没有直接作为参数传递。

 

调用方法:调用对象方法的语法如下:

<idObject>:<idMethod>([<uArgList>])

其中 <uArgList> 是一个可选的逗号分隔参数列表,用于将参数传递给指定的方法。 <idObject> 标识了方法调用要发送的对象;要在方法中引用同一对象,请使用 SELF: 或 SUPER:(见下文注释),而不是 <idObject>:。

 

您可以在表达式中调用方法,也可以作为程序语句调用方法。 如果作为程序语句调用,返回值将被忽略。

 

您也可以将方法调用作为别名表达式使用,方法调用前加上别名并用括号括起来:

<idAlias>->(<idObject>:<idMethod>([<uArgList>]))

执行此操作后,与 <idAlias> 关联的工作区将被选中,方法将被执行,原始工作区将被重新选中。 你可以将别名表达式指定为程序语句,就像指定其他表达式一样。

方法可以递归调用自身。 这意味着你可以在一个方法的 <MethodBody> 中引用该方法。

 

 

使用 HIDDEN 和 PROTECT 修饰符也可以影响类型化方法的可见性,就像使用实例变量一样。 子类的方法不能调用父类中 HIDDEN 方法的超级调用。

 

调用约定:方法使用 CLIPPER 调用约定,除非它们是强类型的。 更多信息,请参阅本指南中的 FUNCTION 语句。

 

SELF 和 SUPER:SELF 是一个特殊变量,其中包含一个对象的引用,该对象是消息的接收者(每次使用发送操作符调用方法或访问实例变量时,都会向对象发送一条消息)。 每当向对象发送一条消息时,都会在调用相应方法前将对象的引用放入 SELF 中。 在类的方法中,必须使用 SELF 和消息发送操作符 (:) 才能向当前对象发送消息。 使用 SELF: 访问实例变量是可选的;有关何时需要使用 SELF: 的详细信息,请参阅本指南中的 ACCESS 和 ASSIGN 条目(始终允许使用)。

 

SUPER 是另一个特殊变量,它包含一个指向方法查找对象最近祖先的类的引用。 它将信息沿着继承树向上传递到相应的超类,只有在当前对象的类继承自另一个类时才有意义。 您可以使用 SUPER 和消息发送操作符 (:) 直接引用超类中定义的方法。 如果在子类中重新定义了一个方法(通过创建与超类中的方法同名的方法),SUPER 是使用超类版本覆盖重新定义的方法的唯一方法。

SUPER: 在定义子类时非常有用,它可以添加一些独特的行为,但仍然继承了超类的标准行为。 例如

CLASS Person
 PROTECT cName AS STRING, symName AS SYMBOL
 
METHOD Init(cFirstName, cLastName) CLASS Person
 cName := cFirstName + " " + cLastName
 symName := String2Symbol(cName)
 
CLASS Customer INHERIT Person
 PROTECT wCustNo AS DWORD
 
METHOD Init(cFirstName, cLastName, nCustNo) ;
         CLASS Customer
 SUPER:Init(cFirstName, cLastName)
 wCustNo := nCustNo

SELF 和 SUPER 变量只允许在方法定义中使用。 SELF 是所有方法的默认返回值。

 

参数:除了在 METHOD 声明语句中指定方法参数外,还可以使用 PARAMETERS 语句来指定参数。 但不推荐这种做法,因为它效率较低,而且不提供编译时完整性验证。 有关详细信息,请参阅本指南中的 PARAMETERS 语句。

强类型方法

 

除了 XSharp 的非类型化方法实现外,现在还支持方法参数和返回值的强类型,这为您提供了一种可以获得高度稳定代码的机制。提供的类型信息使编译器能够执行必要的类型检查,从而保证更高的代码质量稳定性。

 

使用强类型方法的另一个好处是性能。强类型方法的实现假定当程序员使用强类型消息时,编译器可以有效地为相应的方法调用执行早期绑定(early binding)。由于这种实现,强类型方法的调用比相应的非类型化方法调用稍快。然而,这些优势是以失去非类型方法所提供的灵活性为代价的。

 

因此,重要的是要记住,在继承链中交替使用特定方法的强类型和非类型版本既不允许也不可能。

 

XSharp 允许对 METHODs、ACCESSes 和 ASSIGNs 进行强类型化。程序员通过以下两个步骤完成 XSharp 中强类型方法的指定:

 

1.在其相应的类中进行强类型方法的强制声明。
该声明负责在 XSharp 用于调用强类型方法的所谓虚拟表中声明方法的顺序。在子类中重新声明方法是不允许的,因为这会导致编译器的歧义。

 

2.定义强类型方法。
与强类型函数不同,方法类型化要求对方法参数、方法返回值进行强类型化,并指定有效的调用约定。
以下调用约定对强类型方法有效:STRICT、PASCAL 或 CALLBACK。

示例

该示例创建了一个二维坐标类,其中包含初始化坐标、绘制表格和绘制点的方法:

 

FUNCTION Start()
 LOCAL oPointSet AS Point2D
 oPointSet := Point2D{2, 3}
 oPointSet:ShowGrid()
 oPointSet:Plot()
 
CLASS Point2D                        // Define Point2D class
 INSTANCE x, y AS INT
 
METHOD Init(iRow, iCol) CLASS Point2D
 x := iRow
 y := iCol
 RETURN SELF
 
METHOD Plot() CLASS Point2D
 @ x + 11, y + 36 SAY CHR(249)
 
METHOD ShowGrid() CLASS Point2D
 LOCAL iCounter AS INT
 CLS
 FOR iCounter := 1 TO 21
         IF iCounter = 11
                 @ iCounter, 1 SAY REPLICATE(CHR(196), 71)
                 @ iCounter, 36 SAY CHR(197)
         ELSE
                 @ iCounter, 36 SAY CHR(179)
         ENDIF
 NEXT

参见

ACCESS, ASSIGN, CLASS, FUNCTION, PROPERTY, OPERATOR, CONSTRUCTOR, DESTRUCTOR, EVENT