我使用Guids和字符串作为数据结构中的键。在C#中,我花了很多(累积)时间想知道为什么当id是OrderId并且我将它与ContractId匹配时,我正在寻找的id没有事件发生。我想要做的是防止这一整类错误。
想象一下,我与以下基础数据类型签订了合同:
type Contract = { Schedule : Guid; TickTable : Guid; Price : float; Quantity : float }
现在我有两个问题:
let contract =
{ Schedule = Guid.Empty; TickTable = Guid.Empty; Price = 0.; Quantity = 0. }
contract.Schedule = contract.TickTable;; // true - ugh
contract.Price = contract.Quantity;; // true - ugh
我可以解决这样的问题:
[<Measure>] type dollars
[<Measure>] type volume
type Contract =
{ Schedule : Guid; TickTable : Guid;
Price : float<dollars>; Quantity : float<volume> }
现在我们有:
let contract =
{ Schedule = Guid.Empty; TickTable = Guid.Empty;
Price = 0.<dollars>; Quantity = 0.<volume> }
contract.Schedule = contract.TickTable;; // true - ugh
contract.Price = contract.Quantity;; // type mismatch - yay
有没有办法可以装饰Guids所以我得到了类型不匹配?我只是想影响编译时间 - 理想情况下,编译后的代码将与测量单位相同。
我知道我可以执行以下操作,但它看起来很难看,我希望它会导致运行时影响:
[<Measure>] type dollars
[<Measure>] type volume
type ScheduleId = ScheduleKey of Guid
type TickTableId = TickTableKey of Guid
type Contract =
{ Schedule : ScheduleId; TickTable : TickTableId;
Price : float<dollars>; Quantity : float<volume> }
let contract =
{ Schedule = ScheduleKey Guid.Empty; TickTable = TickTableKey Guid.Empty;
Price = 0.<dollars>; Quantity = 0.<volume> }
contract.Schedule = contract.TickTable;; // type error - yay
contract.Price = contract.Quantity;; // type mismatch - yay
答案 0 :(得分:3)
通过编写带有[<Measure>]
类型参数的类型,您可以将任何类型包装为具有单位,甚至通常。此外,正如latkin在评论中暗示的那样,使用struct (which is allocated in place, not as a new object)可以节省额外的分配和间接。
通用度量单位感知包装器:
type [<Struct>] UnitAware<'T, [<Measure>] 'u> =
val Raw : 'T
new (raw) = { Raw = raw }
let withUnit<[<Measure>] 'u> a = UnitAware<_, 'u>(a)
这样,任意类型都可以给出一个度量单位感知值类型包装器,只需通过withUnit<myUnit>
包装并用.Raw
解包:
let a = 146L |> withUnit<dollars>
let b = 146L |> withUnit<volume>
a = b // Type mismatch.
由于结构比较,具有相同单位且内容相同的两个结构包装也将相等。与其他度量单位使用情况一样,在运行时会丢失额外的类型安全性:box a = box b
为真,就像box 1.<dollars> = box 1.<volumes>
一样。