• 话说 type 之 record 记录的使用技巧 F#


    看过 Effective C# 的应该对“第1章条款7:将值类型尽可能实现为具有常量性和原子性的类型”有所感悟。实现一个具有常量性的不可变类型,对于并行程序来说有着说不完的优点。

    这就是为什么 F# 在 class 和 struct 这两个的基础上还给我们提供一个叫 record 记录的类型的原因,它是不可变类型,并且你可以非常方便的创建它,而不需要像 C# 一样写一堆代码。

    比如,创建一个名为 Point 的不可变类型来标记一个点的坐标,你像下面这样写就可以了

    type Point = { x : float; y : float}

    然后,创建一个记录的实例也很方便

    let p = { x = 20.0; y = 20.0 }

    一切看起来都很美,但是真的是这样吗?

    这里做个假设,假设我们想创建一个 x = 20.2; y = 0 的记录,那么我们可不可以只定义 x,而不用管 y,直接用默认值呢?因为本身 float 的默认值就是 0 嘛。

    有这样的想法原因很简单,如果我们要创建一个记录,我们少说一点,有20个成员,那每次创建一个都得写20个等于号,这也太累了吧。

    好,我们试试只定义x。并且为了以示类型的确定性,我们给 p 明确定义了类型。

    let p : Point = { x = 20.0 }

    结果,编译器提示我们出错了:

    No assignment given for field 'y' of type 'Point'

    此路不通,我们再试试给 x 和 y 定义一个 [<DefaultValue>] 的属性呢?

    type Point = { [<DefaultValue>] x : float; [<DefaultValue>] y : float }
    let p : Point = { x = 20.0 }

    结果继续报错

    Extraneous fields have been given values.

    错误告诉我们说 x 已经被定义了,不能再次定义。郁闷!难道不得不用 mutable ?不要!那不是我们想要的。

    =============================

    怀着郁闷的心情请教了微软 F# 项目组的一位大牛,他说他会提交这个 bug, 但是也给了一个目前的解决方法,其实这个方法也还不错,那就是用 with

    type Point = { x : float; y : float }
    let zero = { x = 0.0; y = 0.0}
    let p : Point = { zero with x = 20.0 }

    我们先定义了一个 zero,然后在这个的基础上用 with 语法来做一些小的修改就好了。

    不过这样写还不太美观,我们可以写得更棒一点

    type Point = { x : float; y : float } with static member Zero = { x = 0.0; y = 0.0}
    let p : Point = { Point.Zero with x = 20.0 }

    我们用了一个 static member,说白了就是个静态实例,并且是预先定义好的。

    然后直接用这个实例来简化我们的构造。

    如果是更加复杂的记录,我们可以预先定义几种常用的实例,然后要用的时候用 with 做一些微调即可。

    比如下面这个就是我在 PInvoke 调用 Win32API 时的一个例子:

    [<StructLayout(LayoutKind.Sequential)>]
    type NOTIFYICONDATA = {
    cbSize : uint32
    hWnd : IntPtr
    uID : uint32
    uFlags : uint32
    uCallbackMessage : uint32
    hIcon : IntPtr
    [<MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)>]
    szTip : string
    dwState : uint32
    dwStateMask : uint32
    [<MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)>]
    szInfo : string
    uTimeoutOrVersion : uint32
    [<MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)>]
    szInfoTitle : string
    dwInfoFlags : uint32
    } with
    static member Zero = {
    cbSize = uint32(Marshal.SizeOf(typeof<NOTIFYICONDATA>))
    hWnd = IntPtr.Zero
    uID = 0u
    uFlags = 0u
    uCallbackMessage = 0u
    hIcon = IntPtr.Zero
    szTip = String.Empty
    dwState = 0u
    dwStateMask = 0u
    szInfo = String.Empty
    uTimeoutOrVersion = 0u
    szInfoTitle = String.Empty
    dwInfoFlags = 0u
    }

    定义的时候麻烦了点,以后调用时是不是会方便许多呢。

    希望这篇文章能对您有点帮助。








     

  • 相关阅读:
    浅谈移动通信和无线通信
    jdk8处理时间
    Mysql查询报错:Illegal mix of collations (gbk_chinese_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '='
    linux系统执行mysql脚本:Can't connect to local MySQL server through socket '/tmp/mysql.sock'
    org.springframework.boot.web.server.WebServerException: Unable to create tempDir. java.io.tmpdir is set to C:UsersADMINI~1AppDataLocalTemp2
    java实现每个单词首字母大写
    常用Java技术社区
    Hibernate处理事务并发问题
    Hibernate的检索策略
    Java对象在Hibernate持久化层的状态
  • 原文地址:https://www.cnblogs.com/softcat/p/2344696.html
Copyright © 2020-2023  润新知