【问题标题】:Clone a class instance, changing just a few of the properties克隆一个类实例,只更改一些属性
【发布时间】:2013-12-05 16:14:02
【问题描述】:

我想知道在 F# 中是否有一些用于克隆类实例的糖,只改变一个或几个属性。

我知道在 F# 中可以使用记录:

let p2 = {p1 with Y = 0.0}

【问题讨论】:

  • 我决定使用 OO,以保持我的代码整洁和功能井井有条。
  • @GuyCoder:“对于类型固有的操作,请使用属性和方法。这是特别指出的,因为一些具有函数式编程背景的人避免一起使用面向对象编程,更喜欢包含定义与类型相关的内在函数的一组函数(例如,长度 foo 而不是 foo.Length)。[..] 通常,在 F# 中,使用面向对象编程作为软件工程设备是首选......“ (research.microsoft.com/en-us/um/cambridge/projects/fsharp/…)

标签: f# clone


【解决方案1】:

模拟类的复制和更新表达式的一种方法是使用带有可选参数的复制构造函数。

type Person(first, last, age) =
  new (prototype: Person, ?first, ?last, ?age) =
    Person(defaultArg first prototype.First, 
           defaultArg last prototype.Last, 
           defaultArg age prototype.Age)
  member val First = first
  member val Last = last
  member val Age = age

let john = Person("John", "Doe", 45)
let jane = Person(john, first="Jane")

编辑

您没有要求这样做,但在许多情况下使类可变导致代码更清晰:

type Person(first, last, age) =
  member val First = first with get, set
  member val Last = last with get, set
  member val Age = age with get, set
  member this.Clone() = this.MemberwiseClone() :?> Person

let john = Person("John", "Doe", 45)
let jane = john.Clone() in jane.First <- "Jane"

【讨论】:

  • 好主意。然而,我很惊讶没有一个惯用的表达方式来实现这一点。
  • 认为可以公平地说,F# 的 OO 方面或多或少地复制了主流 .NET 语言。虽然,有一些添加——主要是为了支持更实用的风格——例如默认的对象表达式和不变性。但总的来说,.NET 上的 OO 故事在 F# 中基本保持不变。
  • AFAIK Copy-and-update 表达式已在 Scala 中引入,以鼓励使用具有不可变属性的类。即使只更新了一个属性,复制和更新也鼓励创建新实例,而无需在 create-from-scratch 构造函数中声明所有内容。按照这种思路,我希望 F# 具有惯用的复制和更新表达式。无论如何,您的解决方案很棒。我接受了,再次感谢您的帮助。
  • 不客气。在我看来,类的复制和更新不会在 F# 中提供明显的好处。已经有记录支持这一点。如果您打算经常更改属性,您可以定义一个 Clone 方法并使其可变。我在答案中添加了一个示例,以便您可以比较这两种方法。
  • @Daniel 我真的认为类的复制和更新会带来巨大的好处。它将使与例如 WSDL 和其他服务的互操作变得非常容易,并使 F# 成为数据互操作的“首选语言”。我什至认为我们应该更进一步,使用运算符制作一个 pattern-match-copy 功能来映射到某些属性名称。非结构化或矛盾数据类型转换中的类型安全。
【解决方案2】:

另一种选择是将记录包装在一个类中。像

这样的东西
type PersonState = { FirstName : string; LastName : string; }

type Person private (state : PersonState) =

    new (firstName, lastName) = 
        Person({ FirstName = firstName; LastName = lastName })

    member this.WithFirstName value =  
        Person { state with FirstName = value } 

    member this.WithLastName value  =  
        Person { state with LastName = value } 

    member this.FirstName with get () = state.FirstName
    member this.LastName with get () = state.LastName

用作

let JohnDoe = Person("John", "Doe")
let JaneDoe = JohnDoe.WithFirstName "Jane" 
let JaneLastName = JaneDoe.LastName

这种方法避免了显式克隆和可变性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-05
    • 2015-02-07
    • 2013-04-01
    相关资源
    最近更新 更多