1

我看到你可以强制使用单案例区分联合的构造函数,你可以对多案例做同样的事情吗?

例如

type MemberId = 
  | MemberId of int
  | MemberGuid of Guid

我目前正在尝试fsi这样

val create : int -> T option
val create : Guid -> T option

但我猜像 C#,F# 不允许你根据返回类型重载展开:

val value : T -> string

编辑 - - - - - - - -

MemberId.fsi =

module MemberId
open System
type _T

val createId : int -> _T option
val createGuid : Guid -> _T option

val value : _T -> 'a

MemberId.fs =

module MemberId
open System
type _T = 
    | Id of int
    | MemberGuid of Guid

let createId id = match id with
                | x when x>0 -> Some(Id(id))
                | _ -> None
let createGuid guid = Some(MemberGuid( guid))

let value (e:_T):int = e

似乎很接近,但解包器无法编译,我似乎无法弄清楚如何编写它

TestConsumer MemberIdClient.fs =

module MemberIdClient
open System
open MemberId

let address1 = MemberId.create(-1)
let address2 = MemberId.create(Guid.Empty)

let unwrapped1 = 
  match address1 with
  | MemberId x -> () // compilation error on 'MemberId x'
  | _ -> ()
4

3 回答 3

5

函数不能重载,但方法可以:

type MemberId = 
    private
    | MemberId of int
    | MemberGuid of Guid

    static member create id = MemberId id
    static member create guid = MemberGuid guid
于 2014-06-13T19:58:44.410 回答
3

确实有一种方法可以使用一些内联技巧来重载输出参数:

open System

type MemberId = 
    private
    | MemberId of int
    | MemberGuid of Guid

type Create = Create with
    static member ($) (Create, id  ) = MemberId id
    static member ($) (Create, guid) = MemberGuid guid

type Value = Value with
    static member ($) (Value, d:int ) = function MemberId   id   -> id   | _ -> failwith "Wrong case"
    static member ($) (Value, d:Guid) = function MemberGuid guid -> guid | _ -> failwith "Wrong case"

let inline create x : MemberId   = Create $ x
let inline value  x : 'IntOrGuid = (Value $ Unchecked.defaultof<'IntOrGuid>) x

let a = create 1
let b = create (Guid.NewGuid())

let c:int  = value a
let d:Guid = value b

通过这样做,您可以“重载”函数,甚至在输出参数上。

无论如何,与单一案例 DU 的最大区别在于,现在解包器不是“安全的”,这就是解包器没有什么意义的原因,除非在某些特定情况下。

在这些情况下,您可以考虑使用其他机制来解包这些值,例如公开函数isX或返回选项,这些选项可能会辅以活动模式来解包。

话虽如此,如果您只对“隐藏”构造函数以进行一些验证感兴趣,而不是隐藏 DU,则可以简单地隐藏构造函数,这是一个示例:

open System

type T = 
    | MemberId of int
    | MemberGuid of Guid

// Shadow constructors
let MemberId  x = if x > 0 then Some (MemberId x) else None
let MemberGuid x = Some (MemberGuid x)

let a = MemberId 1
let b = MemberGuid (Guid.NewGuid())
let c = MemberId -1

// but you can still pattern match
let printValue = function
| Some (MemberId   x) -> sprintf "case 1, value is %A" x
| Some (MemberGuid x) -> sprintf "case 2, value is %A" x
| None                -> "No value"

let ra = printValue a  // "case 1, value is 1"
let rb = printValue b  // "case 2, value is 67b36c20-2..."
let rc = printValue c  // "No value"

// and if you want to use an overloaded constructor
type T with
    static member Create id   = MemberId id
    static member Create guid = MemberGuid guid

let d = T.Create 1
let e = T.Create (Guid.NewGuid())

// or using the inline trick
type Create = Create with
    static member ($) (Create, id  ) = MemberId id
    static member ($) (Create, guid) = MemberGuid guid
let inline create x : T option = Create $ x

let d' = create 1
let e' = create (Guid.NewGuid())
于 2014-06-13T21:12:47.130 回答
0

这是我需要的 Gustavo 回答中的一小段代码,它似乎可以自行工作

module MemberId
open System

type MemberId = 
    | MemberId of int
    | MemberGuid of Guid

// Shadow constructors
let MemberId  x = if x > 0 then Some (MemberId x) else None
let MemberGuid x = Some (MemberGuid x)
于 2014-06-16T13:45:34.313 回答