我刚开始使用 F#,关于转换的一些问题让我非常困惑。不幸的是,我的背景阅读试图弄清楚为什么让我更加困惑,所以我正在寻找一些我可以适应一般解释的具体答案......
我有一个 ReadOnlyCollection<'T> 枚举,由这个函数生成:
let GetValues<'T when 'T :> Enum> () =
(new ReadOnlyCollection<'T>(Enum.GetValues (typeof<'T>) :?> 'T[])) :> IList<'T>
我想用它做的是找到它的值使用的枚举的所有位(即,按位或列表中的所有值一起),并将其作为通用枚举类型返回,'T。在我看来,这样做的明显方法是这样的:
let UsedBits<'T when 'T :> Enum> () =
GetValues<'T>()
|> Seq.fold (fun acc a -> acc ||| a) 0
...除了无法编译,并出现错误“声明的类型参数 'T' 不能在此处使用,因为在编译时无法解析类型参数。”
我可以通过首先转换为 Int32 来完成实际工作(我真的不想这样做,因为我希望这个函数可以在所有枚举上工作,而不管底层类型如何),即:
let UsedBits<'T when 'T :> Enum> () =
GetValues<'T>()
|> Seq.map (fun a -> Convert.ToInt32(a))
|> Seq.fold (fun acc a -> acc ||| a) 0
...但结果是作为 Int32 产生的。如果我尝试将其转换回 'T,我将再次遇到编译错误。
我不想在我的问题中过于具体,因为我不确定我应该询问哪些细节,所以 - 这种方法的缺陷在哪里?我该怎么办?
(编辑添加:,发布@Daniel 的回答
唉,这似乎是我对上下文的理解不够好而无法理解答案的情况之一,所以......
我想我了解您的答案中的内联和不同约束正在做什么,但是作为 F# 新手,您是否介意对这些事情进行一些扩展,以便我可以检查我的理解是否偏离基础?谢谢。
)
最佳答案
你可以这样做:
let GetValues<'T, 'U when 'T : enum<'U>>() =
Enum.GetValues(typeof<'T>) :?> 'T[]
let inline GetUsedBits() =
GetValues() |> Seq.reduce (|||)
inline
允许更灵活的约束,即 'T (requires member ( ||| ))
.没有它,编译器必须选择一个可以用 IL 表示的约束,或者,如果不能这样做,则选择一个具体类型。在这种情况下,它选择 int
因为它支持 (|||)
.这是一个更简单的重现:
let Or a b = a ||| b //add 'inline' to compare
见 Statically Resolved Type Parameters在 MSDN 上了解更多信息。
关于在 F# 中与类型参数进行转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14467431/