2013-04-22 2 views
1

Каков наилучший способ создания экземпляра System.Type, представляющего запись или объединение F # во время выполнения? То есть, я ищу эквивалент FSharpType.MakeTupleType для записей и союзов.Создание записи F # или типа объединения (а не ее экземпляра) во время выполнения

Чтобы уточнить, мне не интересно создавать экземпляр (т. Е. FSharpValue.MakeRecord или FSharpValue.MakeUnion).

+0

Возможный дубликат [Создание записи F # через отражение] (http://stackoverflow.com/questions/8139223/creating-f-record-through-reflection) – Daniel

+0

@ daniel приведенный ниже пример действительно завершен и полезен для запуска point – hmwill

ответ

2

Мне не известно об эквиваленте FSharpType.MakeTupleType для записей и объединений в библиотеке F #.

Один из способов создания структур записи или объединения типа во время выполнения - использовать 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>|]) 

Обратите внимание, что ожидаемые интерфейсы для типа записи могут также должны быть сгенерированы, то есть реализации IEquatable, IStructuralEquatable, IComparable и IStructuralComparable отсутствуют ,

Update

Методы расширения MakeTupleType и MakeUnionType на основе образца кода выше, теперь доступны в открытом источнике Fil (F # к IL Compiler) проекта (альфа).

+0

CodePlex отключается, этот ответ ссылается на сообщение «Fil», есть ли новое репо? – Maslow

Смежные вопросы