方法
方法
谢谢。
本文内容
方法是与类型关联的函数。在面向对象编程中,方法用于公开和实现对象和类型的功能和行为。
语法
F#复制
// Instance method definition.
[ attributes ]
member [inline] self-identifier.method-name parameter-list [ : return-type ] =
method-body
// Static method definition.
[ attributes ]
static member [inline] method-name parameter-list [ : return-type ] =
method-body
// Abstract method declaration or virtual dispatch slot.
[ attributes ]
abstract member method-name : type-signature
// Virtual method declaration and default implementation.
[ attributes ]
abstract member method-name : type-signature
[ attributes ]
default self-identifier.method-name parameter-list [ : return-type ] =
method-body
// Override of inherited virtual method.
[ attributes ]
override self-identifier.method-name parameter-list [ : return-type ] =
method-body
// Optional and DefaultParameterValue attributes on input parameters
[ attributes ]
[ modifier ] member [inline] self-identifier.method-name ( input) [ : return-type ]
注意事项
在前面的语法中,您可以看到各种形式的方法声明和定义。在较长的方法体中方法,换行符跟在等号 (=) 之后,整个方法体都是缩进的。
属性可以应用于任何方法声明。它们通常列在方法定义语法之前的单独行上。有关详细信息,请参阅属性。
方法可以用 .有关 的信息,请参阅内联函数。
非内联方法可以在类型中递归使用;无需显式使用 rec 关键字。
实例方法
实例方法声明如下:关键字和自标识符,后跟句点(.)以及方法名称和参数。与 let 绑定一样,参数列表可以是模式。通常,方法参数作为元组括在括号中,这就是在其他 .NET 语言中创建方法时它们在 F# 中的显示方式。但是,柯里化形式(空格分隔的参数)也很常见,并且支持其他模式。
下面的例子说明了非抽象实例方法的定义和使用。
F#复制
type SomeType(factor0: int) =
let factor = factor0
member this.SomeMethod(a, b, c) =
(a + b + c) * factor
member this.SomeOtherMethod(a, b, c) =
this.SomeMethod(a, b, c) * factor
在实例方法中,不要使用自标识符来访问使用 let 绑定定义的字段。访问其他成员和属性时使用自标识符。
静态方法
关键字用于指定可以在没有实例的情况下调用方法,并且该方法不与对象实例关联。否则,该方法是实例方法。
下一节中的示例展示了使用 let 关键字声明的字段、使用该关键字声明的属性成员以及使用该关键字声明的静态方法。
下面的例子说明了静态方法的定义和使用。假设这些方法定义在上一节的类中。
F#复制
static member SomeStaticMethod(a, b, c) =
(a + b + c)
static member SomeOtherStaticMethod(a, b, c) =
SomeType.SomeStaticMethod(a, b, c) * 100
抽象和虚方法
关键字表示该方法有一个虚拟的调度槽,并且可能没有在类中定义。虚拟调度槽是内部维护的函数表中的一个条目,用于在运行时查找面向对象类型的虚拟函数调用。虚拟调度机制是一种实现多态的机制,多态是面向对象编程的一个重要特征。具有至少一个未定义的抽象方法的类是抽象类,这意味着不能创建该类的任何实例。有关抽象类的更多信息,请参阅抽象类。
抽象方法声明不包括方法体。相反,方法的名称后跟一个冒号 (:) 和方法的类型签名。方法的类型签名与您将鼠标悬停在代码编辑器中的方法名称上时显示的相同,但没有参数名称。当您以交互方式工作时,解释器 fsi.exe 还会显示类型签名。方法的类型签名是通过列出参数的类型、返回类型和相应的分隔符来构造的。参数用->分隔,元组参数用*分隔。始终使用 -> 符号将返回值与参数分开。圆括号可用于对复杂参数进行分组,例如函数类型是参数时,或指示元组何时被视为单个参数而不是两个参数。
您还可以通过将定义添加到类并使用关键字来为抽象方法提供默认定义,如本主题中的语法块所示。在同一类中定义的抽象方法等效于其他 .NET 语言中的虚拟方法。无论定义是否存在,关键字都会在类的虚函数表中创建一个新的调度槽。
无论基类是否实现其抽象方法,派生类都可以提供抽象方法的实现。要在派生类中实现抽象方法,请在派生类中定义具有相同名称和签名的方法,但使用 or 关键字,并提供方法体。关键字和具有相同的含义。如果新方法覆盖基类实现,则使用;在与原始抽象声明相同的类中创建实现时使用。在实现基类中声明为抽象方法的方法不要使用关键字。
以下示例演示了一个具有默认实现的抽象方法,该实现等效于 .NET 虚拟方法。
F#复制
type Ellipse(a0 : float, b0 : float, theta0 : float) =
let mutable axis1 = a0
let mutable axis2 = b0
let mutable rotAngle = theta0
abstract member Rotate: float -> unit
default this.Rotate(delta : float) = rotAngle <- rotAngle + delta
以下示例演示了重写基类方法的派生类。在这种情况下,重写会改变行为,因此该方法什么都不做。
F#复制
type Circle(radius : float) =
inherit Ellipse(radius, radius, 0.0)
// Circles are invariant to rotation, so do nothing.
override this.Rotate(_) = ()
重载方法
重载方法是在给定类型中具有相同名称但参数不同的方法。在 F# 中,通常使用可选参数而不是重载方法。但是方法,该语言支持使用重载方法,前提是参数是元组形式,而不是柯里化。
可选参数
从F#4.1开始,方法中也可以使用带有默认参数值的可选参数。这是为了帮助促进与 C# 代码的互操作。以下示例演示了这种语法:
F#复制
// A class with a method M, which takes in an optional integer argument.
type C() =
_.M( i) = i + 1
请注意,为 e 传入的值必须与输入类型匹配。在上面的例子中,它是一个 int。尝试将非整数值传递给 e 将导致编译错误。
示例:属性和方法
以下示例包含具有字段、私有函数、属性和静态方法示例的类型。
F#复制
x1 && y2 > y1) then
Some (RectangleXY(x1, y1, x2, y2))
else
None
result
// Test code.
let testIntersection =
let r1 = RectangleXY(10.0, 10.0, 20.0, 20.0)
let r2 = RectangleXY(15.0, 15.0, 25.0, 25.0)
let r3 : RectangleXY option = RectangleXY.intersection(r1, r2)
match r3 with
| Some(r3) -> printfn "Intersection rectangle: %f %f %f %f" r3.X1 r3.Y1 r3.X2 r3.Y2
| None -> printfn "No intersection found."
testIntersection
">type RectangleXY(x1 : float, y1: float, x2: float, y2: float) =
// Field definitions.
let height = y2 - y1
let width = x2 - x1
let area = height * width
// Private functions.
static let maxFloat (x: float) (y: float) =
if x >= y then x else y
static let minFloat (x: float) (y: float) =
if x <= y then x else y
// Properties.
// Here, "this" is used as the self identifier,
// but it can be any identifier.
member this.X1 = x1
member this.Y1 = y1
member this.X2 = x2
member this.Y2 = y2
// A static method.
static member intersection(rect1 : RectangleXY, rect2 : RectangleXY) =
let x1 = maxFloat rect1.X1 rect2.X1
let y1 = maxFloat rect1.Y1 rect2.Y1
let x2 = minFloat rect1.X2 rect2.X2
let y2 = minFloat rect1.Y2 rect2.Y2
let result : RectangleXY option =
if ( x2 > x1 && y2 > y1) then
Some (RectangleXY(x1, y1, x2, y2))
else
None
result
// Test code.
let testIntersection =
let r1 = RectangleXY(10.0, 10.0, 20.0, 20.0)
let r2 = RectangleXY(15.0, 15.0, 25.0, 25.0)
let r3 : RectangleXY option = RectangleXY.intersection(r1, r2)
match r3 with
| Some(r3) -> printfn "Intersection rectangle: %f %f %f %f" r3.X1 r3.Y1 r3.X2 r3.Y2
| None -> printfn "No intersection found."
testIntersection
另见