【发布时间】:2017-12-10 01:19:20
【问题描述】:
我有以下情况:
1。 有许多 (20 - 40) 非常相似的结构缓慢变化的数据类型。
2。 低级别的每个数据类型都由一个唯一的(对于一个类型)字符串标签和一个唯一的(同样对于一个类型)键(通常是字符串或长整数)表示。密钥不会改变,但标签(平均而言)可能每年更新一次不到。
3。 我将每个这样的数据类型“提升”为 F# DU,以便每个标签/键都有一个 DU 案例。这是必需的,因为一些更高级别的代码需要进行模式匹配。因此,在极少数情况下,当类型更新时,我想要一个编译错误,以防事情发生变化。因此,对于每种此类类型,我都会生成/编写如下代码:
type CollegeSize =
| VerySmall
| Small
| Medium
| Large
| VeryLarge
with
static member all =
[|
(CollegeSize.VerySmall, "Very small (less than 2,000 students)", 1L)
(CollegeSize.Small, "Small (from 2,001 to 5,000 students)", 2L)
(CollegeSize.Medium, "Medium (from 5,001 to 10,000 students)", 3L)
(CollegeSize.Large, "Large (from 10,000 to 20,000 students)", 4L)
(CollegeSize.VeryLarge, "Very large (over 20,000 students)", 5L)
|]
4。 所有这些类型都在两个地方使用:F# 引擎,它进行一些计算,以及一个网站。 F# 引擎仅适用于 DU 案例。不幸的是,如今的网站大多基于字符串。因此,Web 团队想要一个基于字符串的方法,用于几乎所有类型的操作,并且他们使用的是 C#。
5。 所以,我创建了两个通用工厂:
type CaseFactory<'C, 'L, 'K when 'C : comparison and 'L : comparison and 'K : comparison> =
{
caseValue : 'C
label : 'L
key : 'K
}
// DU factory: 'C - case, 'L - label, 'K - key
type UnionFactory< 'C, 'L, 'K when 'C : comparison and 'L : comparison and 'K : comparison> (all : array< 'C * 'L * 'K> ) =
let map = all |> Array.map (fun (c, l, _) -> (c, l)) |> Map.ofArray
let mapRev = all |> Array.map (fun (c, l, _) -> (l, c)) |> Map.ofArray
let mapVal = all |> Array.map (fun (c, _, k) -> (c, k)) |> Map.ofArray
let mapValRev = all |> Array.map (fun (c, _, k) -> (k, c)) |> Map.ofArray
let allListValue : System.Collections.Generic.List<CaseFactory< 'C, 'L, 'K>> =
let x =
all
|> List.ofArray
|> List.sortBy (fun (_, s, _) -> s)
|> List.map (fun (c, s, v) -> { caseValue = c; label = s; key = v } : CaseFactory< 'C, 'L, 'K>)
new System.Collections.Generic.List<CaseFactory< 'C, 'L, 'K>> (x)
//Use key, NOT label to create.
[<CompiledName("FromKey")>]
member this.fromKey (k : 'K) : 'C = mapValRev.Item (k)
// For integer keys you can pass "1" instead of 1
[<CompiledName("FromKeyString")>]
member this.fromKeyString (s : string) : 'C = this.fromKey (convert s)
//Use key, NOT label to create.
[<CompiledName("TryFromKey")>]
member this.tryFromKey (k : 'K) : 'C option = mapValRev.TryFind k
[<CompiledName("TryFromKeyString")>]
member this.tryFromKeyString (s : string) : 'C option = mapValRev.TryFind (convert s)
//Use key, NOT label to create.
[<CompiledName("TryFromKey")>]
member this.tryFromKey (k : 'K option) : 'C option =
match k with
| Some x -> this.tryFromKey x
| None -> None
[<CompiledName("AllList")>]
member this.allList : System.Collections.Generic.List< CaseFactory< 'C, 'L, 'K>> = allListValue
[<CompiledName("FromLabel")>]
member this.fromLabel (l : 'L) : 'C = mapRev.[l]
[<CompiledName("TryFromLabel")>]
member this.tryFromLabel (l : 'L) : 'C option = mapRev.TryFind l
[<CompiledName("TryFromLabel")>]
member this.tryFromLabel (l : 'L option) : 'C option =
match l with
| Some x -> this.tryFromLabel x
| None -> None
[<CompiledName("GetLabel")>]
member this.getLabel (c : 'C) : 'L = map.[c]
[<CompiledName("GetKey")>]
member this.getKey (c : 'C) : 'K = mapVal.[c]
6。 此时,如果我为每种类型创建一个带有单例的通用工厂,那么一切都像时钟一样工作,例如:
type CollegeSizeFactory private () =
inherit UnionFactory<CollegeSize, string, int64> (CollegeSize.all)
static let instance = CollegeSizeFactory ()
7。 …除了网络团队不想在每次调用泛型工厂的方法时都使用实例。所以,我最终写了:
type CollegeSizeFactory private () =
inherit UnionFactory<CollegeSize, string, int64> (CollegeSize.all)
static let instance = CollegeSizeFactory ()
static member Instance = instance
static member FromLabel s = CollegeSizeFactory.Instance.fromLabel s
static member FromKey k = CollegeSizeFactory.Instance.fromKey k
static member FromKeyString s = CollegeSizeFactory.Instance.fromKeyString s
这样做的问题是,如果明天他们需要更多快捷方式(如静态成员 FromLabel s),那么泛型工厂的所有实现都必须手动更新。
理想情况下,我想要一个单行,这样对于我需要的每个工厂,我都可以从泛型类型继承,例如:
type CollegeSizeFactory private () =
inherit UnionFactory<CollegeSize, string, int64> (CollegeSize.all)
然后自动获取可能需要的所有内容,包括所有静态成员。我想知道这是否有可能,如果有,那么究竟如何。
非常感谢。
【问题讨论】:
-
这个问题太长太复杂了。试着想出一个更简单、更小的例子来说明你需要什么和你拥有什么。此外,代码格式已全部关闭。此外,“работает как часы”这一表达并不直接翻译成英语。尝试“像发条一样”或“像魅力一样”。