在 F# 中与类型参数进行转换

标签 casting f# inline

我刚开始使用 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/

相关文章:

c - inline和#define在实践中有什么区别?

c - 转换 void* 缓冲区时无效使用 void 表达式

php - 在 MySQL 或 PHP 中隐藏 CAST SELECT 输出的前导 0

java - 需要从 List<String[]> 转换为数组的帮助

performance - 编译 F# 引用 : performance?

f# - 从 WebSharper 访问 JavaScript 的 this

c++ 函数内嵌在编译时决定

iphone - 在方法之间传递时 float 被破坏(类型转换问题?)

f# - F#中的右结合运算符

c++ - 宏和内联函数在执行速度方面有何区别?