首页 > 代码库 > 9.5.2 使用值和委托

9.5.2 使用值和委托

9.5.2 使用值和委托

 

在这一节,我们要讨论在C# 中使用两个更典型的 F# 构造,如何导出值和高阶函数,后者是棘手的,因为,对于函数,F# 使用相当复杂的内部表示。

如果函数的参数为 int -> int –> int,C# 开发人员会把它看成FastFunc<INT,FastFunc<int, int>>。使用这种类型虽然是可行的,但非常不方便,我们要使用另一种方法。如果要让高阶函数能在 C# 中使用,可以使用标准的 .NET 委托,它并不像在 F# 中使用通常的函数那样自然,但是,这样的库在 C# 中的使用要简单得多。

我们想直接公开值或函数时,另一个问题就出现了:在 .NET 中,方法(和字段)不独自出现,乃至成为命名空间的一部分,它们总是类型的一部分。方法独立于类型存在,足以让富有同情心的 C# 开发人员抓狂;帮助就在眼前,是以 F# 模块的形式。清单 9.22 显示了如何公开值和工具函数,这样就能在 C# 中使用了,还演示了前面对高阶函数使用委托。

 

清单 9.22 公开值和高阶函数 (F#)

 

namespace Chapter09.FSharpExport

open System

 

type Client =

  { Name : string; Income : int;YearsInJob : int

     UsesCreditCard :bool; CriminalRecord : bool }

 

module Tests =    [1]

  let John =

    { Name = "JohnDoe"; Income = 25000; YearsInJob = 1

      UsesCreditCard = true; CriminalRecord = false }

 

  let WithIncome (f:Func<_, _>)client =    [2]

    { client with Income =f.Invoke(client.Income) }    [3]

 

模块声明[1]告诉 F# 编译器把值和函数括到一个有静态方法(编译函数时)和静态属性(值)的类中。在这里,我们选择遵循 C# 的命名规则(使用 Pascalcase,即 Pascal 方法),把创建模块放在首位的原因,是为了把值公开给 C#。

第二点要注意的是 WithIncome 函数,是高阶函数,但没有把通常的 F# 函数作为参数值,而是取有两个泛型参数值的.NET 委托 Func,作为参数值[2]。我们使用下划线,因此,F# 编译器会推断出实际类型。当我们需要在后面的代码中调用这个委托[3],就使用它的 Invoke 方法。这相比正常的 F# 的函数调用,不算优雅,但是,C# 客户端就能以惯用的方式,使用 lambda 函数处理了:

 

var client = Tests.John;

client = Tests.WithIncome(income =>income + 5000, client);

Console.WriteLine("{0} - {1}",client.Name, client.Income);

 

模块我们称为 Tests,编译成类,这样,值 John 成为类的静态属性,WithIncome 成为方法,可以看到,它的参数为 Func<int, int> 类型,所以,任何知道 C# 3.0 的人都可以使用它,即使代码实际上是用 F# 写的。实际上,我们可以把 WithIncome 改成 Client 类型的成员,这样,C# 用户可以使用熟悉的点符号调用了。然而,我们只是为了说明,甚至是基本的 F# 函数也可用于C#,没有任何问题。

9.5.2 使用值和委托