在下面的代码中,我想使用变量的类型实例化一个模板化的类,最终作为传递给函数的泛型变量,但即使这个更简单的形式也不起作用:
type saveXY<'a when 'a:comparison> (x:'a,y:'a) =
member this.X = x
member this.Y = y
member this.lessThan () = this.X < this.Y
[<EntryPoint>]
let main argv =
let x1 = 3
let y1 = 7
let saver1 = new saveXY<int>(x1,y1) // Good
printfn "%A" (saver1.lessThan())
let x2 = 3.0
let y2 = 7.0
let saver2 = new saveXY<float>(x2,y2) // Good
printfn "%A" (saver2.lessThan())
let saver3 = new saveXY<x2.GetType()> (x2,y2) // No Good, see errors
0
然而,对于saver3
以下我得到(我无法找到有关FS1241的信息):
...\Program.fs(23,39): error FS0010: Unexpected symbol '(' in type arguments. Expected ',' or other token.
...\CompareProblem\Program.fs(23,39): error FS1241: Expected type argument or static argument
如果删除saveXY
上的模板,则saver2
出错,因为saver1
导致saveXY
类参数被限制为整数。
我还尝试将x和y简单地声明为obj
,但这并不起作用。我怀疑问题是这只是因为这是不可能的,
也就是说,如果类参数类型是通用的,则它们一次派生(从第一次使用)。另一方面,也许我错过了一些东西。
有没有办法在编译时使用基于变量的类型作为F#中的类型模板参数?是否有另一种方法来创建一个能够处理/存储通用值的类型?
更新:根据Lee的建议,如果您对该类进行模板设置,然后使用<_>
进行实例化,则该方法有效:
type saveXY<'a when 'a:comparison> (x:'a, y:'a) =
member this.X = x
member this.Y = y
member this.lessThan () = this.X < this.Y
[<EntryPoint>]
let main argv =
let x1 = 3
let y1 = 7
let saver3 = new saveXY<_> (x1,y1) // works, 'a is int
printfn "%A" (saver3.lessThan())
let x2 = 3.0
let y2 = 7.0
let saver3 = new saveXY<_> (x2,y2) // works, 'a is float
printfn "%A" (saver3.lessThan())
System.Console.ReadKey() |> ignore // wait for a key
0
但是为什么我需要模拟类类型,如上所述?当我使用<_>
时,似乎编译器正在推断类型,所以为什么我不能简单地使用(就像我对函数一样):
type saveXY(x, y) = // x and y are generic, no? They only require comparison, yes?
member this.X = x
member this.Y = y
member this.lessThan () = this.X < this.Y
[<EntryPoint>]
let main argv =
let x1 = 3
let y1 = 7
let saver3 = new saveXY (x1,y1) // works
printfn "%A" (saver3.lessThan())
let x2 = 3.0
let y2 = 7.0
let saver3 = new saveXY (x2,y2) // But this FAILS with error FS0001
printfn "%A" (saver3.lessThan())
System.Console.ReadKey() |> ignore // wait for a key
0
答案 0 :(得分:2)
如果您只是希望编译器推断出可以使用的泛型参数的类型:
let saver3 = new saveXY<_>(x2, y2)
或
let saver3 = saveXY(x2, y2)
答案 1 :(得分:2)
F#没有任何内置方法来实例化运行时已知类型的通用代码。你总是可以通过反射来做到这一点,但它并不经常使用 - 如果用反射创建它,你实际上无法对该值做太多的事情。
为了示例,您可以将LessThan
移动到单独的界面中:
type ILessThan =
abstract LessThan : unit -> bool
type SaveXY<'T when 'T:comparison> (x:'T,y:'T) =
member this.X = x
member this.Y = y
interface ILessThan with
member this.LessThan () =
printfn "Comparing values of type: %s" (typeof<'T>.Name)
this.X < this.Y
给定表示参数的System.Type
和两个obj
值,您可以在运行时使用SaveXY<T>
作为给定typeof<T>
的反射创建System.Type
的实例}:
let createSaveXY typ x y =
let typ = typedefof<SaveXY<_>>.MakeGenericType [| typ |]
typ.GetConstructors().[0].Invoke([| x; y |]) :?> ILessThan
这是一个如何工作的例子:
let lt = createSaveXY (typeof<float>) (box 3.0) (box 7.0)
lt.LessThan()
但正如我之前所说,这很少有用而且效率不高 - 所以不要复制它,试着描述你想要解决的问题 - 可能有更好的解决方案。