9.1.2 使用类型扩展追加成员

在上一节我们提到过,可以为任何 F# 数据类型添加成员;现在,我们将使用差别联合来演示。这种种方法能够添加成员,而不需要修改任何原始代码。这样,我们将能够保留原始类型和原始的函数声明,不作修改,然后添加成员。

我们将扩展第五章声明 schedule 类型的示例,这个类型表示的事件可以只发生一次,或重复发生,或从不发生。除了数据类型之外,我们还创建了计算事件下一次发生时间的函数。清单 9.4 是代码稍作修改后的版本,我们使代码更紧凑,并使用简单的工具函数,重构了模式匹配中的 Once 分支;如果想比较的话,原始代码在清单 5.5 中。

清单 9.4 有函数的Schedule 数据类型 (F#)

type Schedule =   [1] <-- 声明原始类型

| Never

| Once of DateTime

| Repeatedly of DateTime * TimeSpan

let futureOrMaxValue(dt) =   [2] <-- 实现工具函数

if (dt > DateTime.Now) then dtelse DateTime.MaxValue

let getNextOccurrence(schedule) =   [3] <-- 指定公开的行为

match schedule with

| Never ->DateTime.MaxValue

| Once(eventDate) ->futureOrMaxValue(eventDate)

| Repeatedly(startDate, interval)–>

let secondsFromFirst =(DateTime.Now - startDate).TotalSeconds

let q = max(secondsFromFirst / interval.TotalSeconds) 0.0

startDate.AddSeconds

(interval.TotalSeconds * (Math.Floor(q) + 1.0))

最重要的变化是,我们增加了工具函数 futureOrMaxValue[2]。这个改变并不显著提高可读性,只是用来说明有这种选择。在一个更复杂的项目中,肯定会有一些工具函数。

这个观点就是,在典型的 F# 源文件中,首先声明类型,然后,有一堆工具(私有)函数,再后是我们想要公开为成员的函数[3]。如果我们想利用上一节介绍的方法,把最后的函数变为成员,这是相当困难的。因为成员必须是类型声明的一部分,但是,我们通常想把工具函数放在类型和其成员之间!

要解决这个问题,需要使用固有类型扩展(intrinsic type extensions),它能够为在文件中先声明的类型,添加成员。清单 9.5 显示了我们如何能够为schedule 类型使用扩展。

清单 9.5 利用固有类型扩展添加成员 (F#)

type Schedule =

| Never

| Once of DateTime

| Repeatedly of DateTime *TimeSpan

let futureOrMaxValue(dt) =

(...)

let getNextOccurrence(schedule) =   [1]

(...)

type Schedule with     [2]

member x.GetNextOccurrence() =getNextOccurrence(x)     [3]

member x.OccursNextWeek =                        | [4]

getNextOccurrence(x)< DateTime.Now.AddDays(7.0)    |

对比清单 9.4 的代码,大部分没有改变,为了简洁,所以就省略了;唯一增加的是最后四行代码。第一行[2]定义了类型扩展,告诉 F# 编译器,要把后面的成员添加到指定名字的类型;后面就是正常的成员声明。因为我们已经把核心功能实现为函数[1],成员的实现就简单了[3]。除了得到下一次发生的时间以外,我们还增加了一个属性[4],使用私有函数检查下一次发生的时间是否就在接下来的一周。

如果学过 C# 3.0,就会发现类型扩展和扩展方法之间有相似性。使用类型扩展,可以为其他程序集中现有类型添加方法和属性。前一个清单的情况有所不同,因为我们使用了固有类型扩展。这是一种特殊的情况,即,声明原始类型和扩展在同一个文件中;在这种情况下,F# 编译器会把类型的两部分合并到同一个类中,我们也能访问在类型扩展中类型的私有成员。

清单 9.6 演示了调用清单 9.5 中的成员。使用类型扩展添加的成员,其行为与其他成员一样的,因此,清单没有任何意外惊喜。

清单 9.6 使用成员处理 Schedule  (F# Interactive)

> let sch = Repeatedly(DateTime.Now,TimeSpan(2, 0, 0, 0));;

val sch : Schedule

> sch.OccursNextWeek();;   <-- 使用重复事件进行测试

val it : bool = true

> let sch = Never;;

val sched : Schedule

> sch.OccursNextWeek();;   <-- 测试无计划事件的行为

val it : bool = false

正如我们处理记录时,通常的方法是创建 F# 值;对于在我们示例中的差别联合,就是使用 Repeatedly 或 Never 识别器(我们还可以使用 Once 识别器)。我们有了值以后,就可以使用面向对象的点符号调用它的成员。

正如我们刚看到的,在写成熟代码时,成员是非常有用的,因为它可以把代码包装成结构良好的片断,以方便使用类型。在 F# 的开发过程中,我们通常不首先写有成员的代码,只有代码测试通过以后,才添加成员,API 设计就固定了。我们已经讨论过添加成员的两种方法:

■类型足够简单时,直接在类型声明中追加成员。

■对于复杂的类型,使用固有类型扩展,可以少改变代码。

类型扩展还有一个好处,可以在扩充之前,使用 F# Interactive 工具,测试类型和它的处理函数,因为我们不必一口气声明整个类型。

我们已经知道了,对于把以数据为中心的 F# 代码转变成实际的 .NET 应用程序或组件,成员是非常重要的。现在,我们将把注意力转向以行为中心的应用程序。

时间: 2024-10-01 03:13:09

9.1.2 使用类型扩展追加成员的相关文章

通过栅格类型扩展使ArcGIS 支持更多传感器类型

1  WHAT:什么是栅格类型? ArcGIS海量影像管理解决方案推出已经有一年时间了,相信很多朋友已经对ArcGIS中如何管理海量影像数据有了大致了解.ArcGIS 10.0中推出了适用于海量影像管理的镶嵌数据集模型(Mosaic Dataset),单个镶嵌数据集就可以管理数百万景,不同时相.不同分辨率.不同坐标系.不同空间位置的影像. 为了将各种来源,形式各异的影像数据导入镶嵌数据集中,我们需要为影像指定"栅格类型(Raster Type)".简单说,栅格类型就是对各种影像数据的分

Util应用程序框架公共操作类(十):可空值类型扩展

当你使用可空的值类型时,你会发现取值很不方便,比如Guid? obj,你要从obj中获取值,可以使用Value属性obj. Value,但obj可能为null,这时候就会抛出一个异常. 可空值类型提供了一个HasValue属性,它可以识别出obj是不是一个null值,每当你获取可空值都需要加上这个判断if(value.HasValue){ var value = obj.Value;}. 下面我们通过几个扩展方法,把判断封装起来. 在Util项目中添加Extensions.Nullable.cs

第六章 类型(class)和成员基础

1. 概述 本章讲述如何在一个类型中定义不同种类的成员. 2. 名词解释 3. 主要内容 3.1 类型的各种成员 在一个类型中,可以定义0个或多个以下种类的成员: ① 常量:常量就是指出数据值恒定不变的一个符号.逻辑上讲,常量始终是静态成员. ② 字段:字段表示一个只读或可读/可写的数据值.强烈建议字段都声明为私有. ③ 实例构造器:是将新对象的实例字段初始化为良好初始状态的一种特殊方法. ④ 类型构造器:是将类型的静态字段初始化为良好初始状态的一种特殊方法. ⑤ 方法:方法是一个特殊的函数,作

ES6 (3):类型扩展

一.字符串 模版字符串:反引号(`)标识. $('#result').append(` There are <b>${basket.count}</b> items in your basket, <em>${basket.onSale}</em> are on sale! `); 变量嵌入(定义变量,使用$ 获取): // 字符串中嵌入变量 let name = "Bob", time = "today"; `Hel

C#原始类型扩展方法—this参数修饰符

扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型.重新编译或以其他方式修改原始类型.扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用.对于用 C# 和 Visual Basic 编写的客户端代码,调用扩展方法与调用在类型中实际定义的方法之间没有明显的差异. 扩展方法被定义为静态方法,但它们是通过实例方法语法进行调用的.它们的第一个参数指定该方法作用于哪个类型,并且该参数以 this 修饰符为前缀.仅当您使用 using 指令将命名空间显式导入到源代码中之后,扩

public类型中internal成员

今天遇到一问题,找到下面的两篇文章,研究比较深入,特转了一下, 最近除了搞ASP.NET MVC之外,我也在思考一些编程实践方面的问题.昨天在回家路上,我忽然对一个问题产生了较为清晰的认识.或者说,原先只是有一丝细微的感觉,而现在将它和一些其他的方面进行了联系,也显得颇为“完备”.这就是问题便是:如何对待类中internal成员.我现在认为“类中的internal成员可能是一个坏味道”,换句话说,如果您的类中出现了internal的成员,就可能是设计上的问题了. 可能这个命题说得还有些笼统,所以

C# this用法系列(二) 通过this修饰符为原始类型扩展方法

定义一个静态类,类中定义静态方法,方法中参数类型前边加上this修饰符,即可实现对参数类型的方法扩展 示例如namespace Demo{ // 这里的类必须为静态类 public static class Json { // 方法为静态方法 // this修饰符后边是string类型,即为string类型扩展出了ToJson方法 public static object ToJson(this string Json) { return Json == null ? null : JsonCo

读经典——《CLR via C#》(Jeffrey Richter著) 笔记_类型的各种成员

[Class中,可能包含的成员] 常量, 字段, 实例构造器, 类型构造器, 方法, 操作符重载, 转换操作符, 属性, 事件, 类型(Class)

2-设置文件类型扩展名

开始键 + e 按alt,显示出"工具" 选择"文件夹选项"--"查看"--在"高级设置"里面,在"隐藏已知文件类型的扩展名"前面的复选框,把√去掉即可. 原文地址:https://www.cnblogs.com/WF-chen/p/9330861.html