我有兴趣了解更多有关以下内容的可能性。给定一个约束为obj类型的集合和以下函数:
let myList = new ResizeArray<obj>()
let addToMyListTuple<'a> (item : string * 'a) =
let boxed = box item
let unboxed = unbox<string * 'a> boxed
let item1 = match unboxed with first, _ -> first
Console.WriteLine(sprintf "%A" item1)
myList.Add(boxed)
与这些2的交互给出了预期的结果,并且无论第二部分中的关联类型如何,都可以使用元组的字符串部分。
addToMyListTuple("integer", 3)
addToMyListTuple("float", 3.0)
addToMyListTuple("string", "string")
addToMyListTuple("tuple", (3, "integer"))
但是,我希望可能的是,我可以在稍后的时间与列表中的项目进行交互,并以这样的方式取消对obj的打开,即访问元组的字符串部分是可能的。
myList
|> Seq.iter(fun x ->
let unboxed = unbox<string * 'a> x
let item1 = match unboxed with first, _ -> first
Console.WriteLine(sprintf "%A" item1)
)
运行它会给我编译时警告
This construct causes code to be less generic than indicated by the type annotations. The type variable 'a has been constrained to be type 'obj'.
和运行时异常
System.InvalidCastException: Unable to cast object of type 'System.Tuple`2[System.String,System.Int32]' to type 'System.Tuple`2[System.String,System.Object]'.
还有其他方法可以完成此行为吗?
答案 0 :(得分:2)
如果你展示了更多你正在尝试做的事情,可能有更好的方法使它变成多态(例如,工会),但你可以用类型测试做到这一点:
let tuples = ResizeArray()
let addToTuples (k, v) = tuples.Add(k, box v)
addToTuples ("int", 3)
addToTuples ("float", 3.0)
addToTuples ("string", "foo")
addToTuples ("tuple", (3, "int"))
addToTuples ("option", Some 1)
tuples
|> Seq.iter (fun (s, x) ->
printf "String: %s, Value: " s
match x with
| :? int as i -> printfn "%d" i
| :? float as d -> printfn "%f" d
| :? string as s -> printfn "%s" s
| :? (int * string) as t -> let x, y = t in printfn "(%d, %s)" x y
| _ -> printfn "{%A}" x
)
答案 1 :(得分:2)
当您致电addToMyTupleList<'a>
时,编译器会静态地知道'a
的具体类型(即您正在调用addToMyTupleList<int>
,addToMyTupleList<float>
等)。相比之下,当您尝试在Seq.iter
内进行拆箱时,您希望根据参数的运行时类型确定'a
,而不是{&#39}。这是F#型系统的工作原理。
在我看来,你有几个选择:
string list
来调用sprintf
。< / LI>
使用您在列表中存储的类型更准确一些。对∃'a.string * 'a
类型进行编码(也就是说,它是由字符串和'a
组成的对,对于某些未知的'a
)并存储这些类型的列表。在像Haskell这样的语言中,这并不是太糟糕,但在F#中忠实地编码它是丑陋/令人困惑的:
type ExPair<'x> =
abstract Apply : string * 'a -> 'x
type ExPair =
abstract ForAll : ExPair<'x> -> 'x
let pack p = { new ExPair with
member __.ForAll<'x>(e:ExPair<'x>) : 'x = e.Apply p }
let myList = [pack ("integer", 3)
pack ("float", 3.0)
pack ("string", "string")
pack ("tuple", (3, "integer"))]
myList
|> List.map (fun e ->
e.ForAll { new ExPair<string> with member __.Apply(s,x) = sprintf "%s,%A" s x })