我们知道隐式变换在可控情况下会使代码变得简洁。熟悉C#的都知道C#中可以自定义隐式变换,例如

public class A
{
private int data; public static implicit operator A(int i)
{
return new A{ data = i};
}
}

众所周知,F#本身不会进行任何隐式变换。那F#中是否也可以自定义隐式变换呢?

当然可以。通过定义自己的操作符即可实现。

我们先定义一个转换操作符

let inline (!>) (x:^a) : ^b = ((^a or ^b) : (static member op_Implicit : ^a -> ^b) x)

先看操作符"!>"的签名:有一个类型为a的输入参数,输出参数类型为b。再看定义部分,则是一个类型约束,指示类型a或者b至少有一个类型包含指定签名的成员

static member op_Implicit。然后接着是输入参数x。这些,就足够实现隐式转换。看一个例子

let inline (!>) (x:^a) : ^b = ((^a or ^b) : (static member op_Implicit : ^a -> ^b) x)

type A() = class end
type B() =
static member op_Implicit(a:A) = B()
member this.Add x y = x + y let show (b: B) = b.Add let res = show (!> A())

这段代码输入结果为3。

我们将引入一个术语duck typing,它是有关动态类型的,对一个对象建立某种适用性以达到某些目的。我们知道在正常的类型中,对象的适用性是由对象的类型决定。在duck typing中,对象的适用性在某种意义上由方法和属性决定,而非对象的类型。

在F#中,我们可以利用这种机制创建泛型函数,从而给我们编程带来很大的好处。例如以下例子中我们引入duck typing

// Longhand
let inline implicit< ^a,^b when ^a : (static member op_Implicit : ^b -> ^a)> arg =
( ^a : (static member op_Implicit : ^b -> ^a) arg) // Shorthand - let the compiler do the work
let inline implicit arg =
( ^a : (static member op_Implicit : ^b -> ^a) arg)

(代码参考http://weblogs.asp.net/podwysocki/f-duck-typing-and-structural-typing

利用以上代码我们就可以重写!>操作符

let (!>) : A -> B = implicit

然后运行上面的最后一行代码

let res = show (!> A())  

可以获得同样的结果3

当然,在F#中为了保证类型安全,不推荐使用这种自定义隐式变换特性。

参考:Type Constraints

05-07 15:46