F#新Bug,小心! module 里的泛型变量。
module Mithra =
let Empty<'T> = Seq.empty <'T>
但是,这只是个语法糖而已。把这 F# 代码块编译成一个库,在C#里调用,即可看到,没有 Empty 变量,只有 Empty 函数。也就是说,这个“Empty”在 F# 里面,只是看起来像变量,实际上是在执行一个函数。这有什么关系呢?答:有关系。
// 声明泛型函数变量
let _getBytes<'T> = ref Unchecked.defaultof< 'T->byte[] >
// 泛型变量具现化,赋值
_getBytes<int16> := BitConverter.GetBytes
_getBytes<int32> := BitConverter.GetBytes
_getBytes<int64> := BitConverter.GetBytes
_getBytes<String> := Encoding.UTF8.GetBytes
// 取出函数
let GetBytes<'T> = !_getBytes<'T>
就是因为 _getBytes 不是变量,而是个函数。每次调用_getBytes,都会重新计算,返回一个 null 的引用值。因此上 F#中 module 里允许泛型变量,这不仅仅是个语法糖,还是个有毒的语法糖。使用的时候,必须先理解其实现实质,不然就会蹦出错误。
下面,给出使用泛型类替代泛型变量后,正确的代码:
module Mithra =
type Cache<'T> () =
static memberval GetBytes: 'T -> byte[] = Unchecked.defaultof< 'T ->byte[] > with get, set
Cache<int16>.GetBytes <- BitConverter.GetBytes
Cache<int32>.GetBytes <- BitConverter.GetBytes
Cache<int64>.GetBytes <- BitConverter.GetBytes
Cache<String>.GetBytes <- Encoding.UTF8.GetBytes
let GetBytes<'T> = Cache<'T>.GetBytes