面向对象的思想已经非常成熟,而使用C#的程序员对面向对象也是非常熟悉,所以我就不对面向对象进行介绍了,在这篇文章中将只会介绍面向对象在F#中的使用。
F#是支持面向对象的函数式编程语言,所以你用C#能做的,用F#也可以做,而且通常代码还会更为简洁。我们先看下面这个用C#定义的类,然后用F#来定义。
//定义一个二维点[DebuggerDisplay("({X}, {Y})")]public class Point2D{ // 用于统计已实例化的数量 private static int count = 0; // 原点 public readonly Point2D OriginalPoint = new Point2D(0, 0); // 完整属性X private double x; public double X { get { return this.x; } set { this.x = value; } } // 自动属性Y public double Y { get; set; } // 到原点的距离 public double LengthToOriginal { get { return this.GetDistance(this.OriginalPoint); } } // 默认构造函数 public Point2D() : this(0,0) {} // 包含两个参数的构造函数 public Point2D(double x, double y) { this.X = x; this.Y = y; count++; // 创建新点时,数量递增1 } // 将(x,y)数值转为Point2D对象的静态方法 public static Point2D FromXY(double x, double y) { return new Point2D(x, y); } // 计算到指定点的距离 public virtual double GetDistance(Point2D point) { var xDif = this.X - point.X; var yDif = this.Y - point.Y; var distance = Math.Sqrt(xDif * xDif + yDif * yDif); return distance; } //重写ToString方法 public override string ToString() { return String.Format("Point2D({0}, {1})", this.X, this.Y); } }
定义类在F#中定义类不需要class关键字,除非定义一个空的类。
type Point2D() = class //定义一个空类 endtype internal Point2D() = class //定义一个空的internal类 endtype Point2D() = //定义一个只包含字段x的类 let mutable x = 0.0不像C#,在F#中,类的访问修饰符默认是public的,所以internal就需要手动指定。并且,在F#,不支持protected访问修饰符,在F#中应该使用接口,对象表达式和高阶函数来实现类似功能。
字段(Field)在上一例代码中我们已经定义了一个字段x,但类中用let定义的字段(包括后面介绍的函数)均为private的。若需要定义其他访问修饰符的字段,需要使用val定义。
type Point2D() = [<DefaultValue>] val mutable public x : float其中DefaultValue特性用于将支持零值初始化的类型(具有零值的基元类型、可为null的类等)字段初始化为零值。
属性(Property)和索引器使用member关键字定义属性,注意F#中的属性需要有一个对象标识符(类似C#中的this)作为前缀。
type Point2D() = let mutable x = 0.0 // 定义属性X,其中set为private的。 member this.X with get() = x and private set(v) = x <- v member val Y = 0.0 with get, set //自动属性在F#中,this也可以使用其他名称来代替。除了this(C++和C#的习惯),有的人还使用self(Python等的习惯)、me(VB.NET的习惯)等。若不需要在属性或方法内部使用到,一般还习惯使用__(两个下划线)来作为对象标识符。
而像C#set方法中的value,F#中也需要自己指定,如例子中使用v。当然get和set都可以省略使之成为只写或只读属性。
遗憾的是自动属性中不支持分别定义访问修饰符(如C#中的{get; private set}),只能使用完整属性来定义。
方法(Method)方法同样使用member定义,而静态方法只需在member前添加static关键字。
type Point2D() = …… //因为篇幅省略属性X,Y的代码 // 定义一个函数来获取指定点到当前点的距离 member this.GetDistance (point:Point2D) = let xDif = this.X - point.X let yDif = this.Y - point.Y let distance = sqrt (xDif**2. + yDif**2.) distance static member FromXY (x:double, y:double) = let point = Point2D() point.X <- x point.Y <- y point重载方法F#类中的方法重载与C#一样,只需重新定义一个同名成员函数,且签名不同即可。下面实现一个接受int类型的FromXY方法:
static member FromXY (x:int, y:int) = Point2D.FromXY(float x,float y) 返回目录
构造函数与实例化F#中有两种构造函数,一为主构造函数(也称隐式构造函数),上面例子中在类型定义后面的()即表示一个无参构造函数。
另一种构造函数(显示定义)是可选的,在类里定义一个new函数即可。但new函数必须满足以下条件:
type Point2D (xValue:double, yValue:double) = let mutable x = xValue member val Y = yValue with get, set new() = Point2D(0.0,0.0)其中,调用无参构造函数时,则使用主构造函数实例化,且两个参数均为0.0。
若要为构造函数添加访问修饰符,写在其之前即可。
type internal Point2D internal (xValue:double, yValue:double) = private new() = Point2D(0.0,0.0)需要注意的是,在类型定义之后若不提供主构造函数,F#并不像C#那样有一个无参的构造函数。
下面的代码能通过编译,但你无法进行实例化:
type Point2D = class end在类中的let绑定会在调用主构造函数时运行,但如果需要在主构造函数中执行其他操作,需要使用do绑定。
type Point2D(xValue:double, yValue:double) = let mutable x = xValue static let mutable count = 0 do count <- count + 1注意let代码和do代码必须在member之前。而do语句中必须返回unit,可使用ignore函数丢弃返回值。
非静态的do语句会在实例化时执行,而静态的do语句会在第一次使用该类型时执行。而在没有主构造函数的类中,无法使用let和do语句。
如果需要在do语句中访问其他方法,则需要类级别的对象标识符,在类型定义后使用as关键字指定;若想在非主构造函数中执行额外的代码,使用then关键字:
F#是支持面向对象的函数式编程语言,所以你用C#能做的,用F#也可以做,而且通常代码还会更为简洁。我们先看下面这个用C#定义的类,然后用F#来定义。
//定义一个二维点[DebuggerDisplay("({X}, {Y})")]public class Point2D{ // 用于统计已实例化的数量 private static int count = 0; // 原点 public readonly Point2D OriginalPoint = new Point2D(0, 0); // 完整属性X private double x; public double X { get { return this.x; } set { this.x = value; } } // 自动属性Y public double Y { get; set; } // 到原点的距离 public double LengthToOriginal { get { return this.GetDistance(this.OriginalPoint); } } // 默认构造函数 public Point2D() : this(0,0) {} // 包含两个参数的构造函数 public Point2D(double x, double y) { this.X = x; this.Y = y; count++; // 创建新点时,数量递增1 } // 将(x,y)数值转为Point2D对象的静态方法 public static Point2D FromXY(double x, double y) { return new Point2D(x, y); } // 计算到指定点的距离 public virtual double GetDistance(Point2D point) { var xDif = this.X - point.X; var yDif = this.Y - point.Y; var distance = Math.Sqrt(xDif * xDif + yDif * yDif); return distance; } //重写ToString方法 public override string ToString() { return String.Format("Point2D({0}, {1})", this.X, this.Y); } }
定义类在F#中定义类不需要class关键字,除非定义一个空的类。
type Point2D() = class //定义一个空类 endtype internal Point2D() = class //定义一个空的internal类 endtype Point2D() = //定义一个只包含字段x的类 let mutable x = 0.0不像C#,在F#中,类的访问修饰符默认是public的,所以internal就需要手动指定。并且,在F#,不支持protected访问修饰符,在F#中应该使用接口,对象表达式和高阶函数来实现类似功能。
字段(Field)在上一例代码中我们已经定义了一个字段x,但类中用let定义的字段(包括后面介绍的函数)均为private的。若需要定义其他访问修饰符的字段,需要使用val定义。
type Point2D() = [<DefaultValue>] val mutable public x : float其中DefaultValue特性用于将支持零值初始化的类型(具有零值的基元类型、可为null的类等)字段初始化为零值。
属性(Property)和索引器使用member关键字定义属性,注意F#中的属性需要有一个对象标识符(类似C#中的this)作为前缀。
type Point2D() = let mutable x = 0.0 // 定义属性X,其中set为private的。 member this.X with get() = x and private set(v) = x <- v member val Y = 0.0 with get, set //自动属性在F#中,this也可以使用其他名称来代替。除了this(C++和C#的习惯),有的人还使用self(Python等的习惯)、me(VB.NET的习惯)等。若不需要在属性或方法内部使用到,一般还习惯使用__(两个下划线)来作为对象标识符。
而像C#set方法中的value,F#中也需要自己指定,如例子中使用v。当然get和set都可以省略使之成为只写或只读属性。
遗憾的是自动属性中不支持分别定义访问修饰符(如C#中的{get; private set}),只能使用完整属性来定义。
方法(Method)方法同样使用member定义,而静态方法只需在member前添加static关键字。
type Point2D() = …… //因为篇幅省略属性X,Y的代码 // 定义一个函数来获取指定点到当前点的距离 member this.GetDistance (point:Point2D) = let xDif = this.X - point.X let yDif = this.Y - point.Y let distance = sqrt (xDif**2. + yDif**2.) distance static member FromXY (x:double, y:double) = let point = Point2D() point.X <- x point.Y <- y point重载方法F#类中的方法重载与C#一样,只需重新定义一个同名成员函数,且签名不同即可。下面实现一个接受int类型的FromXY方法:
static member FromXY (x:int, y:int) = Point2D.FromXY(float x,float y) 返回目录
构造函数与实例化F#中有两种构造函数,一为主构造函数(也称隐式构造函数),上面例子中在类型定义后面的()即表示一个无参构造函数。
另一种构造函数(显示定义)是可选的,在类里定义一个new函数即可。但new函数必须满足以下条件:
- 返回类型必须为该类
- 不使用member和对象标识符。
- 使用一个元组或unit作为参数。
type Point2D (xValue:double, yValue:double) = let mutable x = xValue member val Y = yValue with get, set new() = Point2D(0.0,0.0)其中,调用无参构造函数时,则使用主构造函数实例化,且两个参数均为0.0。
若要为构造函数添加访问修饰符,写在其之前即可。
type internal Point2D internal (xValue:double, yValue:double) = private new() = Point2D(0.0,0.0)需要注意的是,在类型定义之后若不提供主构造函数,F#并不像C#那样有一个无参的构造函数。
下面的代码能通过编译,但你无法进行实例化:
type Point2D = class end在类中的let绑定会在调用主构造函数时运行,但如果需要在主构造函数中执行其他操作,需要使用do绑定。
type Point2D(xValue:double, yValue:double) = let mutable x = xValue static let mutable count = 0 do count <- count + 1注意let代码和do代码必须在member之前。而do语句中必须返回unit,可使用ignore函数丢弃返回值。
非静态的do语句会在实例化时执行,而静态的do语句会在第一次使用该类型时执行。而在没有主构造函数的类中,无法使用let和do语句。
如果需要在do语句中访问其他方法,则需要类级别的对象标识符,在类型定义后使用as关键字指定;若想在非主构造函数中执行额外的代码,使用then关键字: