1

実行時に F# レコードまたはユニオンを表す System.Type のインスタンスを作成する最良の方法は何ですか? つまり、レコードとユニオンの FSharpType.MakeTupleType に相当するものを探しています。

明確にするために、インスタンス (つまり、FSharpValue.MakeRecord または FSharpValue.MakeUnion) の作成には興味がありません。

4

1 に答える 1

2

F# ライブラリのレコードとユニオンのFSharpType.MakeTupleTypeに相当するものを認識していません。

実行時にレコードまたはユニオン型のような構造を作成する 1 つの方法は、Reflection.Emitを使用することです。レコード型はシール クラスに似ており、ユニオン型はケースごとにシール クラスを持つ抽象基本クラスです。

たとえば、次の関数は最小限の F# レコード タイプを生成します。

open System
open System.Reflection
open System.Reflection.Emit

let MakeRecord(typeName:string, fields:(string * Type)[]) =
    let name = "GeneratedAssembly"
    let domain = AppDomain.CurrentDomain
    let assembly = domain.DefineDynamicAssembly(AssemblyName(name), AssemblyBuilderAccess.RunAndSave)
    let name = "GeneratedModule"
    let dm = assembly.DefineDynamicModule(name, name+".dll")
    let attributes = TypeAttributes.Public ||| TypeAttributes.Class ||| TypeAttributes.Sealed
    let typeBuilder = dm.DefineType(typeName, attributes)
    let con = typeof<CompilationMappingAttribute>.GetConstructor([|typeof<SourceConstructFlags>|])
    let customBuilder = CustomAttributeBuilder(con, [|SourceConstructFlags.RecordType|])
    typeBuilder.SetCustomAttribute(customBuilder)
    let makeField name t =
        let attributes = FieldAttributes.Assembly
        let fieldBuilder = typeBuilder.DefineField(name+"@", t, attributes)
        let attributes = PropertyAttributes.None
        let propertyBuilder = typeBuilder.DefineProperty(name, attributes, t, [||])
        let customBuilder = CustomAttributeBuilder(con, [|SourceConstructFlags.Field|])
        propertyBuilder.SetCustomAttribute(customBuilder)
        let attributes = MethodAttributes.Public ||| MethodAttributes.HideBySig ||| MethodAttributes.SpecialName
        let methodBuilder = typeBuilder.DefineMethod("get_"+name, attributes, t, [||])
        let il = methodBuilder.GetILGenerator()
        il.Emit(OpCodes.Ldarg_0)
        il.Emit(OpCodes.Ldfld, fieldBuilder)
        il.Emit(OpCodes.Ret)
        propertyBuilder.SetGetMethod(methodBuilder)
        fieldBuilder
    let types = fields |> Array.map snd
    let cb = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, types)
    let il = cb.GetILGenerator()
    il.Emit(OpCodes.Ldarg_0)
    il.Emit(OpCodes.Call, typeof<obj>.GetConstructor(Type.EmptyTypes))
    fields |> Array.iteri (fun i (name, t) -> 
        let paramName = name.Substring(0,1).ToLower()+name.Substring(1)
        let param = cb.DefineParameter(i+1, ParameterAttributes.In, paramName)
        let fieldBuilder = makeField name t
        il.Emit(OpCodes.Ldarg_0)
        il.Emit(OpCodes.Ldarg, param.Position)
        il.Emit(OpCodes.Stfld, fieldBuilder)
    )
    il.Emit(OpCodes.Ret)
    let t = typeBuilder.CreateType()
    assembly.Save("GeneratedModule.dll")
    t

let r = MakeRecord("MyRecord", [|"Alpha",typeof<int>;"Beta",typeof<string>|])

Record 型の予期されるインターフェイスも生成する必要がある場合があることに注意してください。つまり、IEquatable、IStructuralEquatable、IComparable、および IStructuralComparable の実装がありません。

アップデート

上記のコード サンプルに基づく拡張メソッド MakeTupleType および MakeUnionType が、オープン ソースのFil (F# to IL Compiler) プロジェクト (アルファ版) で利用できるようになりました。

于 2013-04-22T07:41:13.920 に答える