热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

F#探险之旅(四):面向对象编程(下)

F#系列随笔索引类的继承在前面(面向对象(中))介绍过的对象表达式中,已经涉及到了类的继承,我们

F#系列随笔索引

类的继承 

在前面(面向对象(中))介绍过的对象表达式中,已经涉及到了类的继承,我们可以覆盖类的成员实现,然后创建新类的实例。这一节里,我们来看看常规的继承机制,熟悉C#的开发人员会感到更亲切。

F# Code - 类的继承
#light
type Base = class
val state : int
new() = { state = 0}
end

type Sub = class
inherit Base
val otherState : int
new() = { otherState = 1 }
end

let myObj = new Sub()


printfn
"myObj.state = %i, myObj.otherState = %i"

myObj.state

myObj.otherState


System.Console.Read()


运行结果为:

Output
myObj.state = 0, myObj.otherState = 1


这里Base类为基类,有一个字段state,Sub继承了它,并有一个新字段otherState,下面的代码可以看到,此时Sub类继承了Base类的state字段。需要注意的是,这里的Base必须要有一个无参的构造函数,否则不能通过编译。其原因是,跟C#一样,在初始化派生类时会调用基类的构造函数,而F#中类没有默认的构造函数,所以必须显式添加构造函数。而如果基类的构造函数带有参数的话,派生类的构造函数写法也有所不同。

F# Code - 基类具有含参的构造函数


运行结果:

Output
Init base class

Init sub
class

myObj.state =
0, myObj.otherState = 1


这个结果说明确实调用了基类的构造函数。Inherit Base(0)则说明,我们必须选择正确的构造函数。

类的方法 

定义方法时可以使用4个相关的关键字,即member、override、abstract、default。在C#中,类的方法大体上可分为三种情况。一是在基类中定义,派生类中不能覆盖的;二是在基类中定义,派生类可以覆盖的(使用virtual关键字);三是抽象方法(或接口方法),派生类需要提供实现的(除非派生类也是抽象类或接口)。对于情况一,可以简单地使用member关键字;情况二,可同时使用abstract和default(或override),abstract说明该方法可以进行覆盖,default则提供了默认实现;情况三,可仅仅使用abstract,同时为类添加特性AbstractClass,说明该方法是抽象类的一个抽象方法,必须在派生类中提供实现。

F# Code - 类继承时的方法


运行结果为:

Output
base class:
1
4

sub
class:
1
1


可以看到JiggleState方法继承了Base的实现,而WiggleState则覆盖了Base的默认实现。

访问基类的成员 

如果你曾写过Page类的派生类,那么很可能写过base.OnLoad()这样的代码。“base”用来引用基类中的成员,在F#中稍有不同。就像没有this一样,base也是没有的,需要给它手工指定名称。

F# Code - 访问基类的成员
#light
open System
open System.Drawing
open System.Windows.Forms

type RectangleForm(color) = class
inherit Form() as base
override x.OnPaint(e) =

e.Graphics.DrawRectangle(color,
10, 10,

x.Width -
30, x.Height - 50)

base.OnPaint(e)
override x.OnResize(e) =

x.Invalidate()

base.OnResize(e)
end

let form = new RectangleForm(Pens.Blue)


[>]
do Application.Run(form)


这里定义Form类的派生类RectangleForm,注意在inherit后面的as base,这里的base就是给基类起的名字,这个名字是任意的。在覆盖OnPaint时就调用了基类的OnPaint方法。

属性和索引器 

属性(Property)和索引器(Indexer)是.NET中重要的“语法糖”特性,它们使得我们的代码更为直观、简洁,而它们本质上是方法。

F# Code - 定义属性


在Properties类中定义属性MyProp,这里我们可以看到熟悉的get/set变成了两个方法get/set,参数y就是C#中的value。

索引器是一种特殊的属性(又称含参属性)。在C#中,索引器本质上是名为Item的方法。在F#中,我们也可以使用Item之外的名字。

F# Code - 定义索引器


这里的Indexers类定义了两个索引器:Item和MyString,通过Reflector可以看到,它们的签名相同,都是“public string this[int i] { get; set; }”,在C#中这是不允许的。C#中索引器的默认名称为Item,对于这里的Item来说,可以用两种方式访问它,而MyString就只有一种了。如果需要考虑同其它语言的兼容性,建议使用Item定义属性

类的静态方法 

静态方法的定义是实例方法类似,不过要用到static关键字。访问静态方法时,不需要建立类的实例,可以通过类直接访问,如下面的ReverseString方法。

F# Code - 定义静态方法


运行结果为:

Output
Hello world


定义委托 

在C#中,通过委托可以像“值”那样去处理方法。显然,在F#中不需要这样,因为函数本来就是当作值来看待的。但是考虑到与其它语言的交互,有时也需要定义委托。

F# Code - 定义委托


这里myInst是委托MyDelegate的一个实例,我们通过Invoke来调用它。运行结果为:

Output
123


定义结构类型 

结构(struct)与类(class)的区别就是我们常说的值类型和引用类型的区别,在此不再赘述。下面定义的结构IpAddress用于表示IP地址。

F# Code - 定义结构类型


定义枚举类型 

枚举类型定义了一组符号名称和数值对,本质上讲,枚举类型就是定义了一组常数字段的结构。F#中枚举的定义也是很简单的。

F# Code - 定义枚举类型
#light

type Season =
| Spring
| Summer
| Autumn
| Winter


如果你希望定义位标记枚举,可以使用System.Flags特性。

小结 

至此,F#中的面向对象编程范式介绍完毕,我们手中的F#也变得更为锐利。本文首先介绍了类的继承、类的方法(虚方法、抽象方法等)、访问基类等跟继承相关的概念;接着是类的属性和索引器、类的静态方法这些类的特殊成员;最后讨论了如何定义委托、结构类型、枚举类型等特殊类型。相信有了这些知识,我们完全可以使用F#代替C#来编写类库了。学习这些知识的过程,也给了我们一个从新的角度了解.NET Framework的机会。在学习了F#的三种主要编程范式之后,下一步该考虑如何在实战中应用它,比如如何组织规模较大的程序,如何建立UI,如何与其它.NET语言进行交互等等,在后续的随笔中将逐步介绍这些内容。

F#系列随笔索引

注意:本文中的代码均在F# 1.9.4.17版本下编写,在F# CTP 1.9.6.0版本下可能不能通过编译。

参考:
《Foundations of F#》 by Robert Pickering
《.NET框架程序设计》 by Jeffery Richter
《Expert F#》 by Don Syme , Adam Granicz , Antonio Cisternino


本文转自一个程序员的自省博客园博客,原文链接:http://www.cnblogs.com/anderslly/archive/2008/10/12/fsharp-adventure-oop-part-three.html,如需转载请自行联系原作者。



推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • springmvc学习笔记(十):控制器业务方法中通过注解实现封装Javabean接收表单提交的数据
    本文介绍了在springmvc学习笔记系列的第十篇中,控制器的业务方法中如何通过注解实现封装Javabean来接收表单提交的数据。同时还讨论了当有多个注册表单且字段完全相同时,如何将其交给同一个控制器处理。 ... [详细]
author-avatar
mobiledu2502925687
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有