F#强制转换为在编译时未知的类型

时间:2014-01-08 14:32:59

标签: .net reflection casting f#

如何投射给定的obj列表。

let l = [1. :> obj; 2. :> obj]

当原始类型(在本例中为float)在编译时不知道时返回浮点列表?

我已经尝试过(表明我对此知之甚少:)):

let t = (l |> List.head).GetType();
l |> List.map (fun e -> e :?> t)

哪个失败了。

let castMe (ty : Type) (arr : obj list)  =
        let m = typeof<Enumerable>.GetMethod("Cast")
        let m = m.MakeGenericMethod([|ty|])
        m.Invoke(null, [|arr|]) :?> System.Collections.Generic.IEnumerable<_>

let t = (l |> List.head).GetType();
l |> castMe t;;

哪个失败了:

  

错误FS0030:值限制。值'it'已推断出来   有通用类型       val it:Generic.IEnumerable&lt;'_ a&gt;将'it'定义为一个简单的数据项,使其成为具有显式参数的函数,或者if   你并不打算将它作为通用的,添加一个类型注释。

=============================================

编辑:

我会尝试解释我想要实现的目标和迄今为止的目标,因为我可能根本没有采取正确的方法。

这是我用来保存我提供类型的数据的结构。

type public InnerData(query, table) =
    ...
    member __.Data = data       // map <string, obj list>
    member __.Headers = headers // Dictionary <string, Type>

这是我的类型提供者的构造函数。

ty.AddMember(ProvidedConstructor([], InvokeCode = fun [] -> <@@ InnerData(queryParam, tableNameParam) :> obj @@>))

这就是我如何定义代表字典键的属性。看看评论。

do mdsTy.DefineStaticParameters([tableNameParam; queueryParam], fun tyName [| :? string as tableNameParam; :? string as queryParam |] ->
        let ty = ProvidedTypeDefinition(
                        asm, 
                        ns, 
                        tyName, 
                        baseType = Some(typeof<obj>))

        let mdsInner = MdsData(mdsQueryParam, tableNameParam)

        for header in mdsInner.Headers do
            let columnName = header.Key
            let columnType = header.Value

            let arrayType = typedefof<IEnumerable<_>>.MakeGenericType(typeof<obj>)
            // This works, but returns obj list. I would like to return a list of columnType that represents the downcasted version of arraytype.

            let property = ProvidedProperty(columnName, arrayType,
                                GetterCode = fun [innerType] -> <@@ ((%%innerType:obj) :?> InnerData).Data.[columnName] @@>)
            ty.AddMember(property)

在这里,我尝试使用上述方法(反射和:?&gt;)来投射数据,但没有任何运气。

2 个答案:

答案 0 :(得分:3)

没有这方面的语法,因为正如Wesley Wiser指出的那样,无论如何你无法用这样的表达做任何事情。但是,由于您正在处理Expr值,因此可以使用Expr.Coerce方法创建与您想要的表达式树相当的表达式树。

答案 1 :(得分:2)

您可以使用通过反射调用的辅助类来执行您最初要求的操作:

type CastHelper<'t>() =
    static member Go(xs : obj list) : 't list =
        xs |> List.map (fun x -> x :?> 't)

let castList (xs : obj list) : obj =

    let typ =
        match xs with
        | [] -> failwithf "Can't cast an empty list"
        | x::xs -> x.GetType()

    let doCast = typedefof<CastHelper<_>>.MakeGenericType([|typ|])
                                         .GetMethod("Go")

    doCast.Invoke(null, [|box xs|])

例如,您的样本数据:

> let xs = [1.0 :> obj ; 2.0 :> obj];;

val xs : obj list = [1.0; 2.0]

> let ys = castList xs;;

val ys : obj = [1.0; 2.0]

> xs.GetType().FullName;;

val it : string =
  "Microsoft.FSharp.Collections.FSharpList`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"

> ys.GetType().FullName;;

val it : string =
  "Microsoft.FSharp.Collections.FSharpList`1[[System.Double, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"

即。结果的动态类型为float list(但静态类型为obj),而原始输入的动态类型为obj list

为了有价值地使用结果,你需要一些关心区别的东西,但这样的代码当然可以存在。