#net #рефлексия #f#
Заинтересовал вопрос как получить информацию о функциях которые содержит модуль. Хочу получить что-то вроде того, что выдает FSI. Например для следующего модуля module Test = let ign _ = () let getNowDateTime() = System.DateTime.Now let getNumbers count = [1..count] FSI выводит такую информацию module Test = begin val ign : 'a -> unit val getNowDateTime : unit -> System.DateTime val getNumbers : count:int -> int list end Пробовал достать данные через рефлексию следующим образом let getInfoAboutModule (t : Type) = let genericToString (t : Type) = match t.GenericTypeArguments with | [| |] -> t.FullName | x -> x |> Seq.map (fun x -> x.Name) |> String.concat "," |> sprintf "%s<%s>" t.Name let getInfo (mi:MethodInfo) = let parameter = let sb = System.Text.StringBuilder() for x in mi.GetParameters() do x.ParameterType |> genericToString |> sprintf "%s : %s ->" x.Name |> sb.Append |> ignore sb.ToString() |> fun str -> if str |> System.String.IsNullOrEmpty then "unit -> " else str sprintf "%s : %s %s" mi.Name parameter (genericToString mi.ReturnType) t.GetMethods(BindingFlags.Public ||| BindingFlags.Static) |> Seq.map getInfo для модуля Test результат будет следующим ign : _arg1 : -> System.Void getNowDateTime : unit -> System.DateTime getNumbers : count : System.Int32 -> FSharpList`1MCVE (ideone) но, естественно, таким образом получаю названия далекие от F#-ных. P.S. Добавлять какие-либо атрибуты к модулю нельзя, т.к. нужно оставить возможность извлекать данные из модулей из других сборок.
Ответы
Ответ 1
Можно воспользоваться FSharp.Compiler.Service для анализа исходных текстов модулей. Если модуль скомпилирован и его исходных текстов нет, то сомневаюсь, что можно добиться лучшего результата, чем ваш. На основе этого примера: open System open System.IO open Microsoft.FSharp.Compiler.SourceCodeServices let text = """ module Test = let ign _ = () let getNowDateTime() = System.DateTime.Now let getNumbers count = [1..count] """ let checker = FSharpChecker.Create() // из примера let parseAndTypeCheckSingleFile input = let file = "test.fsx" let projOptions = checker.GetProjectOptionsFromScript(file, input) |> Async.RunSynchronously let parseFileResults, checkFileResults = checker.ParseAndCheckFileInProject(file, 0, input, projOptions) |> Async.RunSynchronously // Wait until type checking succeeds (or 100 attempts) match checkFileResults with | FSharpCheckFileAnswer.Succeeded(res) -> parseFileResults, res | res -> failwithf "Parsing did not finish... (%A)" res // печать информации о члене модуля let printInfo (fn:FSharpMemberOrFunctionOrValue) = // строка с информацией о типе let rec getTypeString (t:FSharpType) = if t.HasTypeDefinition then let prms = [ for x in t.GenericArguments -> (getTypeString x) ] in t.TypeDefinition.DisplayName + " " + String.Join(" ", prms) else t.ToString() printf "%s: " fn.DisplayName for group in fn.CurriedParameterGroups do for prm in group do match prm.Name with | None -> () | Some name -> printf "%s:" name printf "%s -> " (getTypeString prm.Type) printf "%s\n" (getTypeString fn.ReturnParameter.Type) [] let main argv = let parseFileResults, checkFileResults = parseAndTypeCheckSingleFile text let m = checkFileResults.PartialAssemblySignature.Entities.[0] let fns = m.NestedEntities.[0].MembersFunctionsAndValues for x in fns do printInfo x 0 Результат: ign: type 'a -> unit getNowDateTime: unit -> DateTime getNumbers: count:int -> list int Либо можно поизучать исходники непосредственно FSI. Если возникает ошибка при парсинге, нужно скопировать файлы FSharp.Core.optdata и FSharp.Core.sigdata в каталог с программой. Файлы можно найти в %PROFRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.4.0.0\.
Комментариев нет:
Отправить комментарий