4

我刚开始使用 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# 新手,你介意在这些事情上稍微扩展一下,这样我就可以检查我的理解是否偏离了基础?谢谢。

)

4

1 回答 1

8

你可以这样做:

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

有关详细信息,请参阅MSDN 上的静态解析类型参数。

于 2013-01-22T21:05:22.613 回答