9.2.2 使用接口对象类型

9.2.2 使用接口对象类型


    就像记录和差别联合一样,接口类型使用 type 构造来声明。清单 9.8 显示了我们以前的检查记录类型,转换为一个接口类型。

 

Listing 9.8 Interface representing client test (F#)

 

type ClientTest =
  abstract Check : Client �C> bool
  abstract Report : Client -> unit

 

    在清单 9.8 中的声明是说,实现 ClientTest 接口的任何类型将需要提供两个成员。在这个接口声明中,写的成员用了 abstract 关键字,这意味着,它们还没有实现。这个声明只指定了成员的名字和类型签名。我们没有明确地说,声明一个接口,但是,F# 编译器是聪明的,足以推断出来。例如,如果我们提供了一个成员的实现,编译器会认识到,我们要声明一个抽象类。在通常的 F# 编程实践中,我们需要抽象类,很少实现继承,所以,在这一章中,我们将重点放在处理接口。

    之前,在我们进一步讨论实现之前,需要注意的一点:在 F# 中,声明接口时,不使用 I 前缀,F# 有办法声明类型,并试图统一所有这些(例如,在声明差别联合时,我们不使用 D 前缀,等等)。在写纯粹的 F# 代码时,我们可以打破 .NET 的规则,但是,写 F# 库,假设用于其他 .NET 语言时,应该始终遵循所有标准的 .NET 编码约定,在所有公共 API 中(例如,接口使用 I 前缀)。

    如果我们想创建一个检查,也就是说,在 C# 中检测客户的犯罪记录,我们必须写一个新的类,来实现该接口。F# 支持类不错,但提供了另一种解决方案,称为对象表达式(object expressions)。这是受函数编程启发,往往是更优雅,因为,创建有用的值之前,我们不必要写任何类的声明。

 

对象表达式和 lambda 函数

 

    在解释什么是对象表达式时,接口和函数类型之间的类比是非常有用的。函数类型的签名是以抽象的概念描述它。它指定该函数的参数值的类型,和返回结果的类型。函数的具体代码是当我们创建了一个函数值时提供的,这可以使用一个 lambda 函数完成,它可以是一个返回函数的表达式,或者是 let 绑定,它创建一个命名函数。

    接口是对象的抽象描述。(1)它定义了对象应该有的一些成员,(2)指定其类型。另外,我们在创建一个具体的值时,为成员提供实际的代码。一种选择是写命名类,实现该接口,这是类似于创建命名函数。对象表达式类似于 lambda 函数,它们可以用在代码的任何地方,创建一个值,实现接口,而不必为提供实际代码的类型指定名字。如果你熟悉 Java,F# 中的对象表达式在本质上与 Java 中的匿名类是相同的。

 

    在清单 9.9 中,我们将创建检查,来检测客户的犯罪记录和收入,创建接口值的列表,就像我们先前的记录列表一样。

 

Listing 9.9 Implementing interfaces using object expressions (F# Interactive)

 

> let testCriminal =
     { new ClientTest with
        member x.Check(cl) = cl.CriminalRecord = true
        member x.Report(cl) =
          printfn "'%s' has a criminal record!" cl.Name };;
val testCriminal : ClientTest

> let testIncome =
     { new ClientTest with
        member x.Check(cl) = cl.Income < 30000
        member x.Report(cl) =
          printfn "Income of '%s' is less than 30000!" cl.Name };;
val testCriminal : ClientTest

> let tests = [ testCriminal; testIncome ];;
val tests : ClientTest list

 

    这段代码使用对象表达式,创建了两个值,实现了 ClientTest 的接口类型。每个对象表达式括在大括号中,并首先初始化标题,指定要实现的接口,后面跟关键字 with,然后,是成员的声明。在语法上,这与我们在上一节讨论的类型扩展颇为相似。成员声明为接口指定的成员提供一个实现,所以,清单 9.9 中的表达式实现了成员 Check 和 Report。

    整个对象表达式满足正常的 F# 表达式的定义:只做一件事,就是返回一个值。如果我们看一下从 F# Interactive 的输出,可以看到,它返回一个类型 ClientTest 的值。这是该接口类型,因此,对象表达式返回实现该接口的具体值,就像 lambda 函数返回一个函数值,实现抽象函数类型一样。

 

注意

 

    从技术上来说,F# 编译器创建一个类,实现该接口,对象表达式返回这个类的一个新实例。然而,这个类的声明只是内部的,所以,我们不能直接访问这个类。我们唯一需要了解的是,它实现了指定的接口。

    这是类似于 C# 3.0 中的匿名类型,其中的编译器也在后台创建一个隐藏的类,我们不能直接访问。在 C# 中,我们知道类的属性是什么,但是,有关属性的信息仅在本地方法内部可用。另一方面,在 F# 中,我们知道类实现了哪一个接口,所以,我们可以处理它,而没有任何此类限制。我们可以写方法,返回众所周知的接口类型,并使用对象表达式来实现。

    在本节中,我们学习了如何在 F# 面向行为的应用程序中,使用接口类型,完成迭代开发的最后一步。接口给我们一个惯用的 .NET 解决方案,但 F# 提供的功能,让我们c 自然的方式处理接口,与函数风格是一致的。由于对象表达式,实现接口比构造函数记录更容易。

    在本章的后面,将学习使用接口,使得它可以从 C# 中舒适地调用 F# 代码。我们还没有讨论在 F# 中的类声明,因为,普通的类在纯 F# 项目中,不经常使用,但是,我们在 9.4 节简要地看一下。在此之前,让我们看看如何在使用来自 .NET 库的一些常见的类型时,能够利用对象表达式。

你可能感兴趣的:(接口,职场,使用,休闲,对象类型)