【问题标题】:obj[] and string[] as parametersobj[] 和 string[] 作为参数
【发布时间】:2016-07-17 20:40:38
【问题描述】:

我正在使用Microsoft.FSharp.Reflection.FSharpValue.MakeUnion,这需要Reflection.UnionCaseInfoobj[](可以为空)作为参数。

但是,我得到了一个 Type mismatch. Expecting a obj [] but given a string [] The type 'obj' does not match the type 'string' 使用 string[] 函数的结果调用时。

我可以为这种情况创建的最简单示例如下(我对此进行了测试,但由于标记为 !! 的行无法编译。

let one (a:obj[]) = a |> Array.map (fun o->printfn "%A" o) |> ignore
one [|"a";"b";"c"|] // OK!
let str = [|"a";"b";"c"|] //the equivalent of my function return
one str//!!Type mismatch.

我不确定我是否打算将 string[] 转换/转换为 obj[] 或......好吧,如果我只是在做其他我不知道的错误。

编辑:实际问题如下所述

let split (by:string) (input:string) = System.Text.RegularExpressions.Regex.Split(input,by)

let buildArgs content = 
 match content with
 | "" -> [||]
 | _ -> content |> split " " //Type mismatch

这是我过去解决的问题:有没有更好的方法?

 | _ -> content |> split " "|> Array.map (fun s->s:>obj)//make sure obj[] is returned

Casting and Conversions (F#) 作为参考

这个我也试过了

let buildArgs content :obj[] = ... // Type mismatch

但这也给了我一个错误:

如果我不执行Array.map,请在函数的最后一行键入不匹配。

【问题讨论】:

  • @phillip-trelford - “您还可以更改一个函数以接受任何对象数组,例如 let one (a:#obj[])”(与我的“答案”一起删除 - 恢复为有价值)FSharpValue.MakeUnion 上使用它(除非我弄错了)
  • 您可以创建自己的 MakeUnion 函数,该函数调用 FSharpValue.MakeUnion
  • 托马斯的答案可能是我的默认答案,您也可以将(fun s -> s:>obj) 缩短为box
  • buildArgs 定义没有给我任何类型不匹配 - 它返回一个字符串数组
  • @CodeBeard 最短但可能是buildArgs 最邪恶的解决方法是(content |> split " ") |> box |> unbox

标签: types casting f# type-conversion


【解决方案1】:

我认为您目前的方法很好;有时我发现像[|for str in ... -> box str|] 这样的东西比... |> Array.map (fun str -> box str) 更易读,但你的里程可能会有所不同。至于你为什么会遇到这个问题,这里有两个有点微妙的问题。

正如 Phil Trelford 的 cmets 所暗示的,.NET 类型系统允许将 string[] 视为 obj[](尽管从 F# 执行此操作需要向上转换和向下转换,即使 .NET 类型系统并不那么严格)。在我看来,这种类型系统的“特性”令人厌恶,即使在这种情况下它可能是安全的,我通常也会避免使用它(有关不受欢迎的数组协方差的一致意见,请参阅Covariance and Contravariance in C#, Part Two: Array CovarianceArray covariance: not just ugly, but slow too)。

所以一般来说,string[] 不会被编译器视为obj[]。为什么当你传入[|"a"; "b"; "c"|] 时一切都很好?这里的答案是,在数组文字的特定情况下,如果可以推断出这样的超类型,编译器允许数组表达式的类型成为每个元素类型的超类型(例如,因为它被另一个签名限制为 obj[]方法,如您的情况)。但是,这只适用于数组字面量(即[|e1; e2; ... |] 形式的表达式)。

【讨论】:

  • 这很清楚 - 谢谢。总的来说,我觉得类型系统“任凭摆布”,我不得不解决它,这有点不对。
  • 我明白了。请记住,从语义上讲,字符串数组实际上不是对象数组(尽管这可能有点不直观)。我敢肯定.NET 的设计者是否可以及时返回,他们会引入 ReadOnlyArray 类型并在整个框架的许多地方使用它来代替常规数组(并且字符串 ReadOnlyArrays 确实可以安全地视为对象 ReadOnlyArrays)。可悲的是,我们必须处理我们所拥有的遗产,疣和所有。
  • (fun str -> box str) => box ?
  • @RamonSnir - 是的,与 upcast 相比,使用部分应用程序的能力是装箱的一个优势,当然它只适用于转换为 obj 的情况。
  • let upcast'<'a> (x : 'a) = x 似乎工作正常。例如B : A[B();B();B­()] |> List.­map upcas­t'<A> 被推断为A list 类型。
【解决方案2】:

如果你定义一个变量来保存对象数组,那么你也可以使用类型注释,F#会自动将字符串向上转换为对象:

let values : obj[] = [|"a";"b";"c"|]

更一般地说,如果编译器在找到表达式之前就知道目标类型(它从左到右查找),那么它会自动构建一个对象数组,即使字面量包含字符串。您的问题中的函数调用也是如此。

但是,一旦您创建了 string[] 类型的值,您必须以某种方式将其转换为 obj[](正如其他人所回答的那样)。

【讨论】:

  • 谢谢。我尝试在返回数组的函数上使用此表示法(编辑问题以反映),但这给了我同样的例外。我没有将数组作为let,它是从模式匹配中流出的。
猜你喜欢
  • 2013-01-24
  • 2015-03-07
  • 2013-01-25
  • 1970-01-01
  • 2010-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多