方法 (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 self-identifier.method-name : type-signature

// Virtual method declaration and default implementation.
[ attributes ]
abstract member [inline] self-identifier.method-name : type-signature
[ attributes ]
default member [inline] self-identifier.method-name parameter-list[ : return-type ] =
    method-body

// Override of inherited virtual method.
[ attributes ]
override member [inline] self-identifier.method-name parameter-list [ : return-type ]=
    method-body

备注

在前面的语法中,您可以看到各种形式的方法声明和定义。 在较长的方法体中,等号 (=) 后跟换行符,并且整个方法体将缩进。

可以将特性应用于任何方法声明。 这些特性位于方法定义语法的前面,并且通常列在单独的行中。 有关更多信息,请参见特性 (F#)

可以将方法标记为 inline。 有关 inline 的信息,请参见内联函数 (F#)

可以按递归方式在该类型中使用非内联的方法;无需显式使用 rec 关键字。

实例方法

实例方法是用 member 关键字和 self-identifier 声明的,后跟一个句点 (.) 和方法名称与参数。 与 let 绑定的情况一样,parameter-list 可以为一个模式。 通常,您以元组形式将方法参数括在括号中,如果方法是用其他 .NET Framework 语言创建的,则方法将采用此方式出现在 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 绑定定义的字段。 请在访问其他成员和属性时使用自我标识符。

静态方法

关键字 static 用于指定一个方法可在没有实例的情况下调用,并且未与对象实例关联。 否则,方法为实例方法。

下一节中的示例显示了使用 let 关键字声明的字段、使用 member 关键字声明的属性成员,以及使用 static 关键字声明的静态方法。

下面的示例阐释了静态方法的定义和用法。 假设上一节中的 SomeType 类中有如下方法定义。

static member SomeStaticMethod(a, b, c) =
   (a + b + c)

static member SomeOtherStaticMethod(a, b, c) =
   SomeType.SomeStaticMethod(a, b, c) * 100

抽象方法和虚方法

关键字 abstract 指示方法具有虚拟调度槽,并且可能在类中没有定义。 虚拟调度槽是内部维护的函数表中的一个条目,该条目用于在运行时查找面向对象的类型中的虚函数调用。 虚拟调度机制是一种实现“多态性”的机制,多态性是面向对象的编程中的一项重要功能。 至少具有一个没有定义的抽象方法的类是“抽象类”,这意味着无法创建该类的任何实例。 有关抽象类的更多信息,请参见抽象类 (F#)

抽象方法声明不包括方法体, 而是方法的名称后跟一个冒号 (:) 和方法的类型签名。 方法的类型签名与在 Visual Studio 代码编辑器中将鼠标指针悬停在方法名称上时 IntelliSense 显示的类型签名相同,只是没有参数名称。 当以交互方式工作时,解释器 fsi.exe 也会显示类型签名。 方法的类型签名是通过列出参数的类型(后跟返回类型以及相应的分隔符)而形成的。 扩充参数由 -> 分隔,元组参数由 * 分隔。 返回值始终由 -> 符号与参数隔开。 可以使用括号将复杂的参数分组(例如当函数类型为参数时),或者可以使用括号来指示何时将元组视为单一参数(而不是两个参数)。

您也可以通过向类中添加定义并使用 default 关键字来指定抽象方法默认定义,如本主题中的语法块中所示。 在同一个类中具有定义的抽象方法相当于其他 .NET Framework 语言中的虚方法。 不管定义是否存在,abstract 关键字都会在类的虚函数表中创建新的调度槽。

无论基类是否实现其抽象方法,派生类都可提供抽象方法的实现。 若要在派生类中实现抽象方法,请在派生类中定义一个具有相同名称和签名的方法(但是使用 override 或 default 关键字),并提供方法体。 关键字 override 和 default 的意义完全相同。 如果新方法重写基类实现,请使用 override;在与原始抽象声明相同的类中创建实现时,请使用 default。 对于实现在基类中声明为抽象的方法的方法,请不要使用 abstract 关键字。

下面的示例阐释一个具有默认实现的抽象方法 Rotate,该方法相当于 .NET Framework 的虚方法。

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

下面的示例阐释一个重写基类方法的派生类。 在本例中,override 将更改行为,因此方法不执行任何操作。

type Circle(radius : float) =
    inherit Ellipse(radius, radius, 0.0)
     // Circles are invariant to rotation, so do nothing.
    override this.Rotate(_) = ()

重载方法

重载方法是在给定类型中具有相同名称但参数不同的方法。 在 F# 中,通常会使用可选参数,而不是重载方法。 不过,允许在该语言中使用重载方法,只要参数采用元组形式(而不是扩充形式)即可。

示例:属性和方法

下面的示例包含一个类型,该类型具有字段、私有函数、属性和静态方法的示例。

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

请参见

其他资源

成员 (F#)